From 51b40b7af5af5c2e980e1bc8857b9873af02a44d Mon Sep 17 00:00:00 2001 From: Anatoliy Belyanskiy Date: Sat, 17 Jun 2023 04:36:58 +1000 Subject: [PATCH] Setup --- .gitmodules | 4 + SP_128.ASM | 18709 +++++++++++++++++++++++++++++++++ SP_TRDOS.ASM | 7034 +++++++++++++ SP__48.ASM | 20413 +++++++++++++++++++++++++++++++++++++ Shared_Includes | 1 + TRDOS/TR_MSD_2.ASZ | 609 ++ TRDOS/TR_MSD_3.ASZ | 596 ++ TRDOS/TR_MSD_S.ASZ | 768 ++ TRDOS/TR_RMD_S.ASZ | 480 + ZX_EXP.ASM | 1743 ++++ ZX_EXP/EXP_DCP2.ASM | 452 + ZX_EXP/EXP_FN.ASM | 764 ++ ZX_EXP/EXP_FN2.ASM | 731 ++ ZX_EXP/EXP_HDD.ASM | 673 ++ ZX_EXP/EXP_LP2.ASM | 2012 ++++ ZX_EXP/EXP_PIC2.ASM | 525 + ZX_EXP/EXP_SCR.ASM | 289 + ZX_EXP/FLEX.ASM | 679 ++ ZX_EXP/FONT.ASM | 151 + ZX_EXP/SERVICE.ASM | 789 ++ ZX_EXP/TEST.ASM | 340 + ZX_EXP2.ASM | 87 + inc/SPRINT00.INC | 618 ++ inc/char_codes.inc | 203 + inc/rom_routines.inc | 1160 +++ inc/system_variables.inc | 309 + inc/trdos.inc | 29 + inc/trdos_addresses.inc | 184 + inc/trdos_codes.inc | 53 + 29 files changed, 60405 insertions(+) create mode 100644 .gitmodules create mode 100644 SP_128.ASM create mode 100644 SP_TRDOS.ASM create mode 100644 SP__48.ASM create mode 160000 Shared_Includes create mode 100644 TRDOS/TR_MSD_2.ASZ create mode 100644 TRDOS/TR_MSD_3.ASZ create mode 100644 TRDOS/TR_MSD_S.ASZ create mode 100644 TRDOS/TR_RMD_S.ASZ create mode 100644 ZX_EXP.ASM create mode 100644 ZX_EXP/EXP_DCP2.ASM create mode 100644 ZX_EXP/EXP_FN.ASM create mode 100644 ZX_EXP/EXP_FN2.ASM create mode 100644 ZX_EXP/EXP_HDD.ASM create mode 100644 ZX_EXP/EXP_LP2.ASM create mode 100644 ZX_EXP/EXP_PIC2.ASM create mode 100644 ZX_EXP/EXP_SCR.ASM create mode 100644 ZX_EXP/FLEX.ASM create mode 100644 ZX_EXP/FONT.ASM create mode 100644 ZX_EXP/SERVICE.ASM create mode 100644 ZX_EXP/TEST.ASM create mode 100644 ZX_EXP2.ASM create mode 100644 inc/SPRINT00.INC create mode 100644 inc/char_codes.inc create mode 100644 inc/rom_routines.inc create mode 100644 inc/system_variables.inc create mode 100644 inc/trdos.inc create mode 100644 inc/trdos_addresses.inc create mode 100644 inc/trdos_codes.inc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7355856 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "Shared_Includes"] + branch = main + path = Shared_Includes + url = https://github.com/Tolik-Trek/Shared_Includes.git diff --git a/SP_128.ASM b/SP_128.ASM new file mode 100644 index 0000000..117d2df --- /dev/null +++ b/SP_128.ASM @@ -0,0 +1,18709 @@ +; ************************************** +; *** SPECTRUM 128 ROM 0 DISASSEMBLY *** +; ************************************** + +; The Spectrum ROMs are copyright Amstrad, who have kindly given permission +; to reverse engineer and publish Spectrum ROM disassemblies. + + +; ===== +; NOTES +; ===== + +; ------------ +; Release Date +; ------------ +; 4th February 2019 + +; ------------------------ +; Disassembly Contributors +; ------------------------ +; Matthew Wilson (www.matthew-wilson.net/spectrum/rom/) +; Andrew Owen (cheveron-AT-gmail.com) +; Geoff Wearmouth (gwearmouth-AT-hotmail.com) +; Rui Tunes +; Paul Farrow (www.fruitcake.plus.com) + +; ------- +; Markers +; ------- +; The following markers appear throughout the disassembly: +; +; [...] = Indicates a comment about the code. +; ???? = Information to be determined. +; +; For bugs, the following marker format is used: +; +; [*BUG* - xxxx. Credit: yyyy] = Indicates a confirmed bug, with a description 'xxxx' of it and the discoverer 'yyyy'. +; [*BUG*? - xxxx. Credit: yyyy] = Indicates a suspected bug, with a description 'xxxx' of it and the discoverer 'yyyy'. +; +; Since many of the Spectrum 128 ROM routines were re-used in the Spectrum +2 and +3, where a bug was originally identified +; in the Spectrum +2 or +3 the discoverer is acknowledged along with who located the corresponding bug in the Spectrum 128. +; +; For every bug identified, an example fix is provided and the author acknowledged. Some of these fixes can be made directly within the routines +; affected since they do not increase the length of those routines. Others require the insertion of extra instructions and hence these cannot be +; completely fitted within the routines affected. Instead a jump must be made to a patch routine located within a spare area of the ROM. +; Fortunately there is 0.5K of unused routines located at #2336-#2536 (ROM 0) which are remnants of the original Spanish 128, and another unused routine +; located at #3FC3-#3FCE (ROM 0). This is sufficient space to implement all of the bug fixes suggested. + + +; ================= +; ASSEMBLER DEFINES +; ================= + +;TASM directives: + +; #DEFINE DEFB .BYTE +; #DEFINE DEFW .WORD +; #DEFINE DEFM .TEXT +; #DEFINE DEFS .FILL +; #DEFINE END .END +; #DEFINE EQU .EQU +; #DEFINE ORG .ORG + + +; ============================== +; REFERENCE INFORMATION - PART 1 +; ============================== + +; ========================== +; 128 BASIC Mode Limitations +; ========================== +; There are a number of limitations when using 128 BASIC mode, some of which are not present when using the equivalent 48 BASIC mode operations. +; These are more design decisions than bugs. +; +; - The RAM disk VERIFY command does not verify but simply performs a LOAD. +; - The renumber facility will not renumber line numbers that are defined as an expression, e.g. GO TO VAL "10". +; - The printer output routine cannot handle binary data and hence EPSON printer ESC codes cannot be sent. +; - The Editor has the following limitations: +; - Variables cannot have the same name as a keyword. This only applies when entering a program and not when one is loaded in. +; - Line number 0 is not supported and will not list properly. It is not possible to directly insert such a line, not even in 48 BASIC mode, +; and so line number 0 is not officially supported. +; - There is a practical limitation on the size of lines that can be entered. It is limited to 20 indented rows, which is the size of the editing buffers. +; Typed lines greater than 20 rows get inserted into the BASIC program, but only the first 20 rows are shown on screen. Editing such a line causes +; it to be truncated to 20 rows. There is no warning when the 20 row limit is exceeded. +; - It is not possible to directly enter embedded control codes, or to correctly edit loaded in programs that contain them. Loaded programs that +; contain them will run correctly so long as the lines are not edited. +; - It is not possible to embed the string of characters ">=", "<=" or "<>" into a string or REM statement without them being tokenized +; (this is perhaps more an oversight than a design decision). +; - In 48 BASIC mode if the line '10 REM abc: PRINT xyz' is typed then the word PRINT is stored as a new keyword since the colon (arguably incorrectly) +; reverts to 'K' mode. In 128 BASIC mode, typing the same line stores each letter as a separate character. + + +; ================== +; Timing Information +; ================== +; Clock Speed = 3.54690 MHz (48K Spectrum clock speed was 3.50000 MHz) +; Scan line = 228 T-states (48K Spectrum was 224 T-states). +; TV scan lines = 311 total, 63 above picture (48K Spectrum had 312 total, 64 above picture). + + +; =========== +; I/O Details +; =========== + +; ------------- +; Memory Paging +; ------------- +; Memory paging is controlled by I/O port: +; #7FFD (Out) - Bits 0-2: RAM bank (0-7) to page into memory map at #C000. +; Bit 3 : 0=SCREEN 0 (normal display file in bank 5), 1=SCREEN 1 (shadow display file in bank 7). +; Bit 4 : 0=ROM 0 (128K Editor), 1=ROM 1 (48K BASIC). +; Bit 5 : 1=Disable further output to this port until a hard reset occurs. +; Bit 6-7 : Not used (always write 0). +; +; The Editor ROM (ROM 0) always places a copy of the last value written to port #7FFD +; into new system variable BANK_M (#5B5C). +; +; ---------- +; Memory Map +; ---------- +; ROM 0 or 1 resides at #0000-#3FFF. +; RAM bank 5 resides at #4000-#7FFF always. +; RAM bank 2 resides at #8000-#BFFF always. +; Any RAM bank may reside at #C000-#FFFF. +; +; ------------------- +; Shadow Display File +; ------------------- +; The shadow screen may be active even when not paged into the memory map. +; +; ---------------- +; Contended Memory +; ---------------- +; Physical RAM banks 1, 3, 5 and 7 are contended with the ULA. +; +; ----------------- +; Logical RAM Banks +; ----------------- +; Throughout ROM 0, memory banks are accessed using a logical numbering scheme, which +; maps to physical RAM banks as follows: +; +; Logical Bank Physical Bank +; ------------ ------------- +; #00 #01 +; #01 #03 +; #02 #04 +; #03 #06 +; #04 #07 +; #05 #00 +; +; This scheme makes the RAM disk code simpler than having to deal directly with physical RAM bank numbers. + +; ------------------------- +; AY-3-8912 Sound Generator +; ------------------------- +; The AY-3-8912 sound generator is controlled by two I/O ports: +; #FFFD (Out) - Select a register 0-14. +; #FFFD (In) - Read from the selected register. +; #BFFD (In/Out) - Write to the selected register. The status of the register can also be read back. +; +; The AY-3-8912 I/O port A is used to drive the RS232 and Keypad sockets. +; +; Register Function Range +; -------- -------- ----- +; 0 Channel A fine pitch 8-bit (0-255) +; 1 Channel A course pitch 4-bit (0-15) +; 2 Channel B fine pitch 8-bit (0-255) +; 3 Channel B course pitch 4-bit (0-15) +; 4 Channel C fine pitch 8-bit (0-255) +; 5 Channel C course pitch 4-bit (0-15) +; 6 Noise pitch 5-bit (0-31) +; 7 Mixer 8-bit (see end of file for description) +; 8 Channel A volume 4-bit (0-15, see end of file for description) +; 9 Channel B volume 4-bit (0-15, see end of file for description) +; 10 Channel C volume 4-bit (0-15, see end of file for description) +; 11 Envelope fine duration 8-bit (0-255) +; 12 Envelope course duration 8-bit (0-255) +; 13 Envelope shape 4-bit (0-15) +; 14 I/O port A 8-bit (0-255) +; +; See the end of this document for description on the sound generator registers. +; +; ---------------------------------- +; I/O Port A (AY-3-8912 Register 14) +; ---------------------------------- +; This controls the RS232 and Keypad sockets. +; Select the port via a write to port #FFFD with 14, then read via port #FFFD and write via port #BFFD. The state of port #BFFD can also be read back. +; +; Bit 0: KEYPAD CTS (out) - 0=Spectrum ready to receive, 1=Busy +; Bit 1: KEYPAD RXD (out) - 0=Transmit high bit, 1=Transmit low bit +; Bit 2: RS232 CTS (out) - 0=Spectrum ready to receive, 1=Busy +; Bit 3: RS232 RXD (out) - 0=Transmit high bit, 1=Transmit low bit +; Bit 4: KEYPAD DTR (in) - 0=Keypad ready for data, 1=Busy +; Bit 5: KEYPAD TXD (in) - 0=Receive high bit, 1=Receive low bit +; Bit 6: RS232 DTR (in) - 0=Device ready for data, 1=Busy +; Bit 7: RS232 TXD (in) - 0=Receive high bit, 1=Receive low bit +; +; See the end of this document for the pinouts for the RS232 and KEYPAD sockets. + +; ------------------ +; Standard I/O Ports +; ------------------ +; See the end of this document for descriptions of the standard Spectrum I/O ports. + + +; ================== +; Error Report Codes +; ================== + +; --------------------------- +; Standard Error Report Codes +; --------------------------- +; See the end of this document for descriptions of the standard error report codes. + +; ---------------------- +; New Error Report Codes +; ---------------------- +; a - MERGE error MERGE! would not execute for some reason - either size or file type wrong. +; b - Wrong file type A file of an inappropriate type was specified during RAM disk operation, for instance a CODE file in LOAD!"name". +; c - CODE error The size of the file would lead to an overrun of the top of memory. +; d - Too many brackets Too many brackets around a repeated phrase in one of the arguments. +; e - File already exists The file name specified has already been used. +; f - Invalid name The file name specified is empty or above 10 characters in length. +; g - File does not exist [Never used by the ROM]. +; h - File does not exist The specified file could not be found. +; i - Invalid device The device name following the FORMAT command does not exist or correspond to a physical device. +; j - Invalid baud rate The baud rate for the RS232 was set to 0. +; k - Invalid note name PLAY came across a note or command it didn't recognise, or a command which was in lower case. +; l - Number too big A parameter for a command is an order of magnitude too big. +; m - Note out of range A series of sharps or flats has taken a note beyond the range of the sound chip. +; n - Out of range A parameter for a command is too big or too small. If the error is very large, error L results. +; o - Too many tied notes An attempt was made to tie too many notes together. +; p - (c) 1986 Sinclair Research Ltd This error is given when too many PLAY channel strings are specified. Up to 8 PLAY channel strings are supported +; by MIDI devices such as synthesisers, drum machines or sequencers. Note that a PLAY command with more than 8 strings +; cannot be entered directly from the Editor. The Spanish 128 produces "p Bad parameter" for this error. It could be +; that the intention was to save memory by using the existing error message of "Q Parameter error" but the change of report +; code byte was overlooked. + + +; ================ +; System Variables +; ================ + +; -------------------- +; New System Variables +; -------------------- +; These are held in the old ZX Printer buffer at #5B00-#5BFF. +; Note that some of these names conflict with the system variables used by the ZX Interface 1. + +SWAP_ROM EQU #5B00 ; 20 Swap paging subroutine. +YOUNGER EQU #5B14 ; 9 Return paging subroutine. +ONERR EQU #5B1D ; 18 Error handler paging subroutine. +PIN EQU #5B2F ; 5 RS232 input pre-routine. +POUT EQU #5B34 ; 22 RS232 token output pre-routine. This can be patched to bypass the control code filter. +POUT2 EQU #5B4A ; 14 RS232 character output pre-routine. +TARGET EQU #5B58 ; 2 Address of subroutine to call in ROM 1. +RETADDR EQU #5B5A ; 2 Return address in ROM 0. +BANK_M EQU #5B5C ; 1 Copy of last byte output to I/O port #7FFD. +RAMRST EQU #5B5D ; 1 Stores instruction RST #08 and used to produce a standard ROM error. +RAMERR EQU #5B5E ; 1 Error number for use by RST #08 held in RAMRST. +BAUD EQU #5B5F ; 2 Baud rate timing constant for RS232 socket. Default value of 11. [Name clash with ZX Interface 1 system variable at #5CC3] +SERFL EQU #5B61 ; 2 Second character received flag: + ; Bit 0 : 1=Character in buffer. + ; Bits 1-7: Not used (always hold 0). + ; #5B62 ; Received Character. +COL EQU #5B63 ; 1 Current column from 1 to WIDTH. +WIDTH EQU #5B64 ; 1 Paper column width. Default value of 80. [Name clash with ZX Interface 1 Edition 2 system variable at #5CB1] +TVPARS EQU #5B65 ; 1 Number of inline parameters expected by RS232 (e.g. 2 for AT). +FLAGS3 EQU #5B66 ; 1 Flags: [Name clashes with the ZX Interface 1 system variable at #5CB6] + ; Bit 0: 1=BASIC/Calculator mode, 0=Editor/Menu mode. + ; Bit 1: 1=Auto-run loaded BASIC program. [Set but never tested by the ROM] + ; Bit 2: 1=Editing RAM disk catalogue. + ; Bit 3: 1=Using RAM disk commands, 0=Using cassette commands. + ; Bit 4: 1=Indicate LOAD. + ; Bit 5: 1=Indicate SAVE. + ; Bit 6; 1=Indicate MERGE. + ; Bit 7: 1=Indicate VERIFY. +N_STR1 EQU #5B67 ; 10 Used by RAM disk to store a filename. [Name clash with ZX Interface 1 system variable at #5CDA] + ; Used by the renumber routine to store the address of the BASIC line being examined. +HD_00 EQU #5B71 ; 1 Used by RAM disk to store file header information (see RAM disk Catalogue section below for details). [Name clash with ZX Interface 1 system variable at #5CE6] + ; Used as column pixel counter in COPY routine. + ; Used by FORMAT command to store specified baud rate. + ; Used by renumber routine to store the number of digits in a pre-renumbered line number reference. [Name clash with ZX Interface 1 system variable at #5CE7] +HD_0B EQU #5B72 ; 2 Used by RAM disk to store header info - length of block. + ; Used as half row counter in COPY routine. + ; Used by renumber routine to generate ASCII representation of a new line number. +HD_0D EQU #5B74 ; 2 Used by RAM disk to store file header information (see RAM disk Catalogue section below for details). [Name clash with ZX Interface 1 system variable at #5CE9] +HD_0F EQU #5B76 ; 2 Used by RAM disk to store file header information (see RAM disk Catalogue section below for details). [Name clash with ZX Interface 1 system variable at #5CEB] + ; Used by renumber routine to store the address of a referenced BASIC line. +HD_11 EQU #5B78 ; 2 Used by RAM disk to store file header information (see RAM disk Catalogue section below for details). [Name clash with ZX Interface 1 system variable at #5CED] + ; Used by renumber routine to store existing VARS address/current address within a line. +SC_00 EQU #5B7A ; 1 Used by RAM disk to store alternate file header information (see RAM disk Catalogue section below for details). +SC_0B EQU #5B7B ; 2 Used by RAM disk to store alternate file header information (see RAM disk Catalogue section below for details). +SC_0D EQU #5B7D ; 2 Used by RAM disk to store alternate file header information (see RAM disk Catalogue section below for details). +SC_0F EQU #5B7F ; 2 Used by RAM disk to store alternate file header information (see RAM disk Catalogue section below for details). +OLDSP EQU #5B81 ; 2 Stores old stack pointer when TSTACK in use. +SFNEXT EQU #5B83 ; 2 End of RAM disk catalogue marker. Pointer to first empty catalogue entry. +SFSPACE EQU #5B85 ; 3 Number of bytes free in RAM disk (3 bytes, 17 bit, LSB first). +ROW01 EQU #5B88 ; 1 Stores keypad data for row 3, and flags: + ; Bit 0 : 1=Key '+' pressed. + ; Bit 1 : 1=Key '6' pressed. + ; Bit 2 : 1=Key '5' pressed. + ; Bit 3 : 1=Key '4' pressed. + ; Bits 4-5: Always 0. + ; Bit 6 : 1=Indicates successful communications to the keypad. + ; Bit 7 : 1=If communications to the keypad established. +ROW23 EQU #5B89 ; 1 Stores keypad key press data for rows 1 and 2: + ; Bit 0: 1=Key ')' pressed. + ; Bit 1: 1=Key '(' pressed. + ; Bit 2: 1=Key '*' pressed. + ; Bit 3: 1=Key '/' pressed. + ; Bit 4: 1=Key '-' pressed. + ; Bit 5: 1=Key '9' pressed. + ; Bit 6: 1=Key '8' pressed. + ; Bit 7: 1=Key '7' pressed. +ROW45 EQU #5B8A ; 1 Stores keypad key press data for rows 4 and 5: + ; Bit 0: Always 0. + ; Bit 1: 1=Key '.' pressed. + ; Bit 2: Always 0. + ; Bit 3: 1=Key '0' pressed. + ; Bit 4: 1=Key 'ENTER' pressed. + ; Bit 5: 1=Key '3' pressed. + ; Bit 6: 1=Key '2' pressed. + ; Bit 7: 1=Key '1' pressed. +SYNRET EQU #5B8B ; 2 Return address for ONERR routine. +LASTV EQU #5B8D ; 5 Last value printed by calculator. +RNLINE EQU #5B92 ; 2 Address of the length bytes in the line currently being renumbered. +RNFIRST EQU #5B94 ; 2 Starting line number when renumbering. Default value of 10. +RNSTEP EQU #5B96 ; 2 Step size when renumbering. Default value of 10. +STRIP1 EQU #5B98 ; 32 Used as RAM disk transfer buffer (32 bytes to #5BB7). + ; Used to hold Sinclair stripe character patterns (16 bytes to #5BA7). + ; ... +TSTACK EQU #5BFF ; n Temporary stack (grows downwards). The byte at #5BFF is not actually used. + +; ------------------------- +; Standard System Variables +; ------------------------- +; These occupy addresses #5C00-#5CB5. +; See the end of this document for descriptions of the standard system variables. + +; ------------------ +; RAM Disk Catalogue +; ------------------ +; The catalogue can occupy addresses #C000-#EBFF in physical RAM bank 7, starting at #EBFF and growing downwards. +; +; Each entry contains 20 bytes: +; Bytes #00-#09: Filename. +; Bytes #0A-#0C: Start address of file in RAM disk area. +; Bytes #0D-#0F: Length of file in RAM disk area. +; Bytes #10-#12: End address of file in RAM disk area (used as current position indicator when loading/saving). +; Byte #13 : Flags: +; Bit 0 : 1=Entry requires updating. +; Bits 1-7: Not used (always hold 0). +; +; The catalogue can store up to 562 entries, and hence the RAM disk can never hold more than 562 files no matter +; how small the files themselves are. Note that filenames are case sensitive. +; +; The shadow screen (SCREEN 1) also resides in physical RAM bank 7 and so if more than 217 catalogue +; entries are created then SCREEN 1 will become corrupted [Credit: Toni Baker, ZX Computing Monthly]. +; However, since screen 1 cannot be used from BASIC, it may have been a design decision to allow the +; RAM disk to overwrite it. +; +; The actual files are stored in physical RAM banks 1, 3, 4 and 6 (logical banks 0, 1, 2, 3), +; starting from #C000 in physical RAM bank 1 and growing upwards. +; +; A file consists of a 9 byte header followed by the data for the file. The header bytes +; have the following meaning: +; Byte #00 : File type - #00=Program, #01=Numeric array, #02=Character array, #03=Code/Screen$. +; Bytes #01-#02: Length of program/code block/screen$/array (#1B00 for screen$). +; Bytes #03-#04: Start of code block/screen$ (#4000 for screen$). +; Bytes #05-#06: Offset to the variables (i.e. length of program) if a program. For an array, #05 holds the variable name. +; Bytes #07-#08: Auto-run line number for a program (#80 in high byte if no auto-run). + +; -------------------------- +; Editor Workspace Variables +; -------------------------- +; These occupy addresses #EC00-#FFFF in physical RAM bank 7, and form a workspace used by 128 BASIC Editor. +; +; #EC00 3 Byte 0: Flags used when inserting a line into the BASIC program (first 4 bits are mutually exclusive). +; Bit 0: 1=First row of the BASIC line off top of screen. +; Bit 1: 1=On first row of the BASIC line. +; Bit 2: 1=Using lower screen and only first row of the BASIC line visible. +; Bit 3: 1=At the end of the last row of the BASIC line. +; Bit 4: Not used (always 0). +; Bit 5: Not used (always 0). +; Bit 6: Not used (always 0). +; Bit 7: 1=Column with cursor not yet found. +; Byte 1: Column number of current position within the BASIC line being inserted. Used when fetching characters. +; Byte 2: Row number of current position within the BASIC line is being inserted. Used when fetching characters. +; #EC03 3 Byte 0: Flags used upon an error when inserting a line into the BASIC program (first 4 bits are mutually exclusive). +; Bit 0: 1=First row of the BASIC line off top of screen. +; Bit 1: 1=On first row of the BASIC line. +; Bit 2: 1=Using lower screen and only first row of the BASIC line visible. +; Bit 3: 1=At the end of the last row of the BASIC line. +; Bit 4: Not used (always 0). +; Bit 5: Not used (always 0). +; Bit 6: Not used (always 0). +; Bit 7: 1=Column with cursor not yet found. +; Byte 1: Start column number where BASIC line is being entered. Always holds 0. +; Byte 2: Start row number where BASIC line is being entered. +; #EC06 2 Count of the number of editable characters in the BASIC line up to the cursor within the Screen Line Edit Buffer. +; #EC08 2 Version of E_PPC used by BASIC Editor to hold last line number entered. +; #EC0C 1 Current menu index. +; #EC0D 1 Flags used by 128 BASIC Editor: +; Bit 0: 1=Screen Line Edit Buffer (including Below-Screen Line Edit Buffer) is full. +; Bit 1: 1=Menu is displayed. +; Bit 2: 1=Using RAM disk. +; Bit 3: 1=Current line has been altered. +; Bit 4: 1=Return to calculator, 0=Return to main menu. +; Bit 5: 1=Do not process the BASIC line (used by the Calculator). +; Bit 6: 1=Editing area is the lower screen, 0=Editing area is the main screen. +; Bit 7: 1=Waiting for key press, 0=Got key press. +; #EC0E 1 Mode: +; #00 = Edit Menu mode. +; #04 = Calculator mode. +; #07 = Tape Loader mode. [Effectively not used as overwritten by #FF] +; #FF = Tape Loader mode. +; #EC0F 1 Main screen colours used by the 128 BASIC Editor - alternate ATTR_P. +; #EC10 1 Main screen colours used by the 128 BASIC Editor - alternate MASK_P. +; #EC11 1 Temporary screen colours used by the 128 BASIC Editor - alternate ATTR_T. +; #EC12 1 Temporary screen colours used by the 128 BASIC Editor - alternate MASK_T. +; #EC13 1 Temporary store for P_FLAG: +; Bit 0: 1=OVER 1, 0=OVER 0. +; Bit 1: Not used (always 0). +; Bit 2: 1=INVERSE 1, INVERSE 0. +; Bit 3: Not used (always 0). +; Bit 4: 1=Using INK 9. +; Bit 5: Not used (always 0). +; Bit 6: 1=Using PAPER 9. +; Bit 7: Not used (always 0). +; #EC14 1 Not used. +; #EC15 1 Holds the number of editing lines: 20 for the main screen, 1 for the lower screen. +; #EC16 735 Screen Line Edit Buffer. This represents the text on screen that can be edited. It holds 21 rows, +; with each row consisting of 32 characters followed by 3 data bytes. Areas of white +; space that do not contain any editable characters (e.g. the indent that starts subsequent +; rows of a BASIC line) contain the value #00. +; Data Byte 0: +; Bit 0: 1=The first row of the BASIC line. +; Bit 1: 1=Spans onto next row. +; Bit 2: Not used (always 0). +; Bit 3: 1=The last row of the BASIC line. +; Bit 4: 1=Associated line number stored. +; Bit 5: Not used (always 0). +; Bit 6: Not used (always 0). +; Bit 7: Not used (always 0). +; Data Bytes 1-2: Line number of corresponding BASIC line (stored for the first row of the BASIC line only, holds #0000). +; #EEF5 1 Flags used when listing the BASIC program: +; Bit 0 : 0=Not on the current line, 1=On the current line. +; Bit 1 : 0=Previously found the current line, 1=Not yet found the current line. +; Bit 2 : 0=Enable display file updates, 1=Disable display file updates. +; Bits 3-7: Not used (always 0). +; #EEF6 1 Store for temporarily saving the value of TVFLAG. +; #EEF7 1 Store for temporarily saving the value of COORDS. +; #EEF9 1 Store for temporarily saving the value of P_POSN. +; #EEFA 2 Store for temporarily saving the value of PR_CC. +; #EEFC 2 Store for temporarily saving the value of ECHO_E. +; #EEFE 2 Store for temporarily saving the value of DF_CC. +; #EF00 2 Store for temporarily saving the value of DF_CCL. +; #EF01 1 Store for temporarily saving the value of S_POSN. +; #EF03 2 Store for temporarily saving the value of SPOSNL. +; #EF05 1 Store for temporarily saving the value of SCR_CT. +; #EF06 1 Store for temporarily saving the value of ATTR_P. +; #EF07 1 Store for temporarily saving the value of MASK_P. +; #EF08 1 Store for temporarily saving the value of ATTR_T. +; #EF09 1512 Used to store screen area (12 rows of 14 columns) where menu will be shown. +; The rows are stored one after the other, with each row consisting of the following: +; - 8 lines of 14 display file bytes. +; - 14 attribute file bytes. +; #F4F1-#F6E9 Not used. 505 bytes. +; #F6EA 2 The jump table address for the current menu. +; #F6EC 2 The text table address for the current menu. +; #F6EE 1 Cursor position info - Current row number. +; #F6EF 1 Cursor position info - Current column number. +; #F6F0 1 Cursor position info - Preferred column number. Holds the last user selected column position. The Editor will attempt to +; place the cursor on this column when the user moves up or down to a new line. +; #F6F1 1 Edit area info - Top row threshold for scrolling up. +; #F6F2 1 Edit area info - Bottom row threshold for scrolling down. +; #F6F3 1 Edit area info - Number of rows in the editing area. +; #F6F4 1 Flags used when deleting: +; Bit 0 : 1=Deleting on last row of the BASIC line, 0=Deleting on row other than the last row of the BASIC line. +; Bits 1-7: Not used (always 0). +; #F6F5 1 Number of rows held in the Below-Screen Line Edit Buffer. +; #F6F6 2 Intended to point to the next location to access within the Below-Screen Line Edit Buffer, but incorrectly initialised to #0000 by the routine at #30D6 (ROM 0) and then never used. +; #F6F8 735 Below-Screen Line Edit Buffer. Holds the remainder of a BASIC line that has overflowed off the bottom of the Screen Line Edit Buffer. It can hold 21 rows, with each row +; consisting of 32 characters followed by 3 data bytes. Areas of white space that do not contain any editable characters (e.g. the indent that starts subsequent rows of a BASIC line) +; contain the value #00. +; Data Byte 0: +; Bit 0: 1=The first row of the BASIC line. +; Bit 1: 1=Spans onto next row. +; Bit 2: Not used (always 0). +; Bit 3: 1=The last row of the BASIC line. +; Bit 4: 1=Associated line number stored. +; Bit 5: Not used (always 0). +; Bit 6: Not used (always 0). +; Bit 7: Not used (always 0). +; Data Bytes 1-2: Line number of corresponding BASIC line (stored for the first row of the BASIC line only, holds #0000). +; #F9D7 2 Line number of the BASIC line in the program area being edited (or #0000 for no line). +; #F9DB 1 Number of rows held in the Above-Screen Line Edit Buffer. +; #F9DC 2 Points to the next location to access within the Above-Screen Line Edit Buffer. +; #F9DE 700 Above-Screen Line Edit Buffer. Holds the rows of a BASIC line that has overflowed off the top of the Screen Line Edit Buffer. +; It can hold 20 rows, with each row consisting of 32 characters followed by 3 data bytes. Areas of white space that do not +; contain any editable characters (e.g. the indent that starts subsequent rows of a BASIC line) contain the value #00. +; Data Byte 0: +; Bit 0: 1=The first row of the BASIC line. +; Bit 1: 1=Spans onto next row. +; Bit 2: Not used (always 0). +; Bit 3: 1=The last row of the BASIC line. +; Bit 4: 1=Associated line number stored. +; Bit 5: Not used (always 0). +; Bit 6: Not used (always 0). +; Bit 7: Not used (always 0). +; Data Bytes 1-2: Line number of corresponding BASIC line (stored for the first row of the BASIC line only, holds #0000). +; #FC9A 2 The line number at the top of the screen, or #0000 for the first line. +; #FC9E 1 #00=Print a leading space when constructing keyword. +; #FC9F 2 Address of the next character to fetch within the BASIC line in the program area, or #0000 for no next character. +; #FCA1 2 Address of the next character to fetch from the Keyword Construction Buffer, or #0000 for no next character. +; #FCA3 11 Keyword Construction Buffer. Holds either a line number or keyword string representation. +; #FCAE-#FCFC Construct a BASIC Line routine. <<< RAM routine - See end of file for description >>> +; #FCFD-#FD2D Copy String Into Keyword Construction Buffer routine. <<< RAM routine - See end of file for description >>> +; #FD2E-#FD69 Identify Character Code of Token String routine. <<< RAM routine - See end of file for description >>> +; #FD6A 1 Flags used when shifting BASIC lines within edit buffer rows [Redundant]: +; Bit 0 : 1=Set to 1 but never reset or tested. Possibly intended to indicate the start of a new BASIC line and hence whether indentation required. +; Bit 1-7: Not used (always 0). +; #FD6B 1 The number of characters to indent subsequent rows of a BASIC line by. +; #FD6C 1 Cursor settings (indexed by IX+#00) - initialised to #00, but never used. +; #FD6D 1 Cursor settings (indexed by IX+#01) - number of rows above the editing area. +; #FD6E 1 Cursor settings (indexed by IX+#02) - initialised to #00 (when using lower screen) or #14 (when using main screen), but never subsequently used. +; #FD6F 1 Cursor settings (indexed by IX+#03) - initialised to #00, but never subsequently used. +; #FD70 1 Cursor settings (indexed by IX+#04) - initialised to #00, but never subsequently used. +; #FD71 1 Cursor settings (indexed by IX+#05) - initialised to #00, but never subsequently used. +; #FD72 1 Cursor settings (indexed by IX+#06) - attribute colour. +; #FD73 1 Cursor settings (indexed by IX+#07) - screen attribute where cursor is displayed. +; #FD74 9 The Keyword Conversion Buffer holding text to examine to see if it is a keyword. +; #FD7D 2 Address of next available location within the Keyword Conversion Buffer. +; #FD7F 2 Address of the space character between words in the Keyword Conversion Buffer. +; #FD81 1 Keyword Conversion Buffer flags, used when tokenizing a BASIC line: +; Bit 0 : 1=Buffer contains characters. +; Bit 1 : 1=Indicates within quotes. +; Bit 2 : 1=Indicates within a REM. +; Bits 3-7: Not used (always reset to 0). +; #FD82 2 Address of the position to insert the next character within the BASIC line workspace. The BASIC line +; is created at the spare space pointed to by E_LINE. +; #FD84 1 BASIC line insertion flags, used when inserting a characters into the BASIC line workspace: +; Bit 0 : 1=The last character was a token. +; Bit 1 : 1=The last character was a space. +; Bits 2-7: Not used (always 0). +; #FD85 2 Count of the number of characters in the typed BASIC line being inserted. +; #FD87 2 Count of the number of characters in the tokenized version of the BASIC line being inserted. +; #FD89 1 Holds '<' or '>' if this was the previously examined character during tokenization of a BASIC line, else #00. +; #FD8A 1 Locate Error Marker flag, holding #01 is a syntax error was detected on the BASIC line being inserted and the equivalent position within +; the typed BASIC line needs to be found with, else it holds #00 when tokenizing a BASIC line. +; #FD8B 2 Stores the stack pointer for restoration upon an insertion error into the BASIC line workspace. +; #FD8C-#FF23 Not used. 408 bytes. +; #FF24 2 Never used. An attempt is made to set it to #EC00. This is a remnant from the Spanish 128, which stored the address of the Screen Buffer here. +; The value is written to RAM bank 0 instead of RAM bank 7, and the value never subsequently accessed. +; #FF26 2 Not used. +; #FF28-#FF60 Not used. On the Spanish 128 this memory holds a routine that copies a character into the display file. The code to copy to routine into RAM, +; and the routine itself are present in ROM 0 but are never executed. <<< RAM routine - See end of file for description >>> +; #FF61-#FFFF Not used. 159 bytes. + + +; ======================== +; Called ROM 1 Subroutines +; ======================== + +ERROR_1 EQU #0008 +PRINT_A_1 EQU #0010 +GET_CHAR EQU #0018 +NEXT_CHAR EQU #0020 +BC_SPACES EQU #0030 +TOKENS EQU #0095 +BEEPER EQU #03B5 +BEEP EQU #03F8 +SA_ALL EQU #075A +ME_CONTRL EQU #08B6 +SA_CONTROL EQU #0970 +PRINT_OUT EQU #09F4 +PO_T_UDG EQU #0B52 +PO_MSG EQU #0C0A +TEMPS EQU #0D4D +CLS EQU #0D6B +CLS_LOWER EQU #0D6E +CL_ALL EQU #0DAF +CL_ATTR EQU #0E88 +CL_ADDR EQU #0E9B +CLEAR_PRB EQU #0EDF +ADD_CHAR EQU #0F81 +ED_ERROR EQU #107F +CLEAR_SP EQU #1097 +KEY_INPUT EQU #10A8 +KEY_M_CL EQU #10DB +MAIN_4 EQU #1303 +ERROR_MSGS EQU #1391 +MESSAGES EQU #1537 +REPORT_J EQU #15C4 +OUT_CODE EQU #15EF +CHAN_OPEN EQU #1601 +CHAN_FLAG EQU #1615 +POINTERS EQU #1664 +CLOSE EQU #16E5 +MAKE_ROOM EQU #1655 +LINE_NO EQU #1695 +SET_MIN EQU #16B0 +SET_WORK EQU #16BF +SET_STK EQU #16C5 +OPEN EQU #1736 +LIST_5 EQU #1822 +NUMBER EQU #18B6 +LINE_ADDR EQU #196E +EACH_STMT EQU #198B +NEXT_ONE EQU #19B8 +RECLAIM EQU #19E5 +RECLAIM_2 EQU #19E8 +E_LINE_NO EQU #19FB +OUT_NUM_1 EQU #1A1B +CLASS_01 EQU #1C1F +VAL_FET_1 EQU #1C56 +CLASS_04 EQU #1C6C +EXPT_2NUM EQU #1C7A +EXPT_1NUM EQU #1C82 +EXPT_EXP EQU #1C8C +CLASS_09 EQU #1CBE +FETCH_NUM EQU #1CDE +USE_ZERO EQU #1CE6 +STOP EQU #1CEE +F_REORDER EQU #1D16 +LOOK_PROG EQU #1D86 +NEXT EQU #1DAB +PASS_BY EQU #1E39 +RESTORE EQU #1E42 +REST_RUN EQU #1E45 +RANDOMIZE EQU #1E4F +CONTINUE EQU #1E5F +GO_TO EQU #1E67 +COUT EQU #1E7A ; Should be OUT but renamed since some assemblers detect this as an instruction. +POKE EQU #1E80 +FIND_INT2 EQU #1E99 +TEST_ROOM EQU #1F05 +PAUSE EQU #1F3A +PRINT_2 EQU #1FDF +PR_ST_END EQU #2048 +STR_ALTER EQU #2070 +INPUT_1 EQU #2096 +IN_ITEM_1 EQU #20C1 +CO_TEMP_4 EQU #21FC +BORDER EQU #2294 +PIXEL_ADDR EQU #22AA +PLOT EQU #22DC +PLOT_SUB EQU #22E5 +CIRCLE EQU #2320 +DR_3_PRMS EQU #238D +LINE_DRAW EQU #2477 +SCANNING EQU #24FB +SYNTAX_Z EQU #2530 +LOOK_VARS EQU #28B2 +STK_VAR EQU #2996 +STK_FETCH EQU #2BF1 +D_RUN EQU #2C15 +ALPHA EQU #2C8D +NUMERIC EQU #2D1B +STACK_BC EQU #2D2B +FP_TO_BC EQU #2DA2 +PRINT_FP EQU #2DE3 +HL_MULT_DE EQU #30A9 +STACK_NUM EQU #33B4 +TEST_ZERO EQU #34E9 +KP_SCAN EQU #3C01 +TEST_SCREEN EQU #3C04 +CHAR_SET EQU #3D00 + + +;************************************************** + +; ========================= +; RESTART ROUTINES - PART 1 +; ========================= +; RST #10, #18 and #20 call the equivalent subroutines in ROM 1, via RST #28. +; +; RST #00 - Reset the machine. +; RST #08 - Not used. Would have invoked the ZX Interface 1 if fitted. +; RST #10 - Print a character (equivalent to RST #10 ROM 1). +; RST #18 - Collect a character (equivalent to RST #18 ROM 1). +; RST #20 - Collect next character (equivalent to RST #20 ROM 1). +; RST #28 - Call routine in ROM 1. +; RST #30 - Not used. +; RST #38 - Not used. + +;[v]..................................................................................................................... SP2000 [v] + MACRO CALL_R1 adr + RST #28 + DEFW adr + ENDM + + DEFINE START_PAUSE #0000 + INCLUDE 'shared_includes/constants/sp2000.inc' + INCLUDE 'inc/char_codes.inc' +;[^]..................................................................................................................... SP2000 [^] + +; ----------------------- +; RST #00 - Reset Machine +; ----------------------- + + ORG #0000 + +L0000: DI ; Ensure interrupts are disabled. + LD BC,START_PAUSE ;#692B + +L0004: DEC BC ; Delay about 0.2s to allow screen switching mechanism to settle. + LD A,B ; + OR C ; + JR NZ,L0004 ; [There is no RST #08. No instruction fetch at #0008 hence ZX Interface 1 will not be paged in from this ROM. Credit: Paul Farrow]. + + JP L00C7 ; to the main reset routine. + +; L000C: DEFB #00, #00 ; [Spare bytes] +; DEFB #00, #00 ; +; + + +; + BLOCK #10-$,#FF +; --------------------------- +; RST #10 - Print A Character +; --------------------------- +L0010: RST #28 ; Call corresponding routine in ROM 1. + DEFW PRINT_A_1 ; #0010. + RET ; + +;[v]..................................................................................................................... SP2000 [v] +RSTx28.part2_3: + EX (SP),HL ; Stack HL. + JP SWAP_ROM ; #5B00. Switch to other ROM (ROM 1) and return to address to call. +;[^]..................................................................................................................... SP2000 [^] +; + + +; + BLOCK #18-$,0 +; ----------------------------- +; RST #18 - Collect A Character +; ----------------------------- +L0018: RST #28 ; Call corresponding routine in ROM 1. + DEFW GET_CHAR ; #0018. + RET ; + +;[v]..................................................................................................................... SP2000 [v] +;L001C +RSTx28.part1_2: + LD H,(HL) ; Fetch the second address byte. + LD L,A ; HL=Subroutine to call. + JR RSTx28.part1_3 +;[^]..................................................................................................................... SP2000 [^] +; + + +; + BLOCK #20-$,0 +; -------------------------------- +; RST #20 - Collect Next Character +; -------------------------------- +L0020: RST #28 ; Call corresponding routine in ROM 1. + DEFW NEXT_CHAR ; #0020. + RET ; + +;[v]..................................................................................................................... SP2000 [v] +;L0024 +RSTx28.part1_3: + POP AF ; Restore AF. + JP RSTx28.part2 ; Jump ahead to continue. +;[^]..................................................................................................................... SP2000 [^] +; + + +; + BLOCK #28-$,0 +; ------------------------------- +; RST #28 - Call Routine in ROM 1 +; ------------------------------- +; RST 28 calls a routine in ROM 1 (or alternatively a routine in RAM while +; ROM 1 is paged in). Call as follows: RST 28 / DEFW address. +;!TODO эта процедура разбита на куски Иваном. Может можно сделать менее раздробленно? +;L0028 +RSTx28: EX (SP),HL ; Get the address after the RST #28 into HL, +; ; saving HL on the stack. + PUSH AF ; Save the AF registers. + LD A,(HL) ; Fetch the first address byte. + INC HL ; Point HL to the byte after + INC HL ; the required address. + LD (RETADDR),HL ; #5B5A. Store this in RETADDR. +R30: DEC HL ; (There is no RST #30) +;[v]..................................................................................................................... SP2000 [v] + ; LD H,(HL) ; Fetch the second address byte. + ; LD L,A ; HL=Subroutine to call. + ; POP AF ; Restore AF. + ; JP RSTx28.part2 ; Jump ahead to continue. + JR RSTx28.part1_2 +RSTx28.part2_2: + LD HL,(TARGET) ; #5B58. HL=Retrieve address to call. [There is no NMI code. Credit: Andrew Owen]. + JR RSTx28.part2_3 +; L0037: DEFB #00 ; [Spare byte] +;[^]..................................................................................................................... SP2000 [^] +; + + +; + BLOCK #38-$,0 +; ========================== +; MASKABLE INTERRUPT ROUTINE +; ========================== +; This routine preserves the HL register pair. It then performs the following: +; - Execute the ROM switching code held in RAM to switch to ROM 1. +; - Execute the maskable interrupt routine in ROM 1. +; - Execute the ROM switching code held in RAM to return to ROM 0. +; - Return to address #0048 (ROM 0). + +L0038: PUSH HL ; Save HL register pair. + LD HL,L0048 ; Return address of #0048 (ROM 0). + PUSH HL ; + LD HL,SWAP_ROM ; #5B00. Address of swap ROM routine held in RAM at #5B00. + PUSH HL ; + LD HL,L0038 ; Maskable interrupt routine address #0038 (ROM 0). + PUSH HL ; + JP SWAP_ROM ; #5B00. Switch to other ROM (ROM 1) via routine held in RAM at #5B00. + +L0048: POP HL ; Restore the HL register pair. + RET ; End of interrupt routine. + + +; =============================== +; ERROR HANDLER ROUTINES - PART 1 +; =============================== + +; ------------------ +; 128K Error Routine +; ------------------ + +L004A: LD BC,#7FFD ; + XOR A ; ROM 0, Bank 0, Screen 0, 128K mode. + DI ; Ensure interrupts are disabled whilst paging. + OUT (C),A ; + LD (BANK_M),A ; #5B5C. Note the new paging status. + EI ; Re-enable interrupts. + DEC A ; A=#FF. + LD (IY+#00),A ; Set ERR_NR to no error (#FF). + JP L0321 ; Jump ahead to continue. + + +; ========================= +; RESTART ROUTINES - PART 2 +; ========================= + +; ----------------------------------------- +; Call ROM 1 Routine (RST #28 Continuation) +; ----------------------------------------- +; Continuation from routine at #0028 (ROM 0). +;L005C +RSTx28.part2: + LD (TARGET),HL ; #5B58. Save the address in ROM 0 to call. + LD HL,YOUNGER ; #5B14. HL='Return to ROM 0' routine held in RAM. + EX (SP),HL ; Stack HL. + PUSH HL ; Save previous stack address. +;[v]..................................................................................................................... SP2000 [v] + ; LD HL,(TARGET) ; #5B58. HL=Retrieve address to call. [There is no NMI code. Credit: Andrew Owen]. + ; EX (SP),HL ; Stack HL. + ; JP SWAP_ROM ; #5B00. Switch to other ROM (ROM 1) and return to address to call. + JR RSTx28.part2_2 +; + +; + BLOCK #66-$,0 +NMI_POINT: + RETN +;[^]..................................................................................................................... SP2000 [^] +; + + +; + BLOCK #6B-$,0 +; ============ +; RAM ROUTINES +; ============ +; The following code will be copied to locations #5B00 to #5B57, within the old ZX Printer buffer. + +; ----------------- +; Swap to Other ROM (copied to #5B00) +; ----------------- +; Switch to the other ROM from that currently paged in. +; !FIXIT!!! vvvv +; [The switching between the two ROMs invariably enables interrupts, which may not always be desired +; (see the bug at #09CD (ROM 0) in the PLAY command). To overcome this issue would require a rewrite +; of the SWAP routine as follows, but this is larger than the existing routine and so cannot simply be +; used in direct replacement of it. A work-around solution is to poke a JP instruction at the start of +; the SWAP routine in the ZX Printer buffer and direct control to the replacement routine held somewhere +; else in RAM. Credit: Toni Baker, ZX Computing Monthly] +; +; [However, the PLAY command bug may be fixed in another manner within the PLAY command itself, in which +; case there is no need to modify the SWAP routine.] +; DISP SWAP_ROM +; SWAP_ROM: +; PUSH AF ; Stack AF. +; PUSH BC ; Stack BC. +; +; LD A,R ; P/V flag=Interrupt status. +; PUSH AF ; Stack interrupt status. +; +; LD BC,#7FFD ; BC=Port number required for paging. +; LD A,(BANK_M) ; A=Current paging configuration. +; XOR #10 ; Complement 'ROM' bit. +; DI ; Disable interrupts (in case an interrupt occurs between the next two instructions). +; LD (BANK_M),A ; Store revised paging configuration. +; OUT (C),A ; Page ROM. +; +; POP AF ; P/V flag=Former interrupt status. +; JP PO,SWAP_EXIT ; Jump if interrupts were previously disabled. +; +; EI ; Re-enable interrupts. +; +; SWAP_EXIT: +; POP BC ; Restore BC. +; POP AF ; Restore AF. +; RET ; +; ENT + +;SWAP_ROM +L006B: PUSH AF ; Save AF and BC. + PUSH BC ; + LD BC,#7FFD ; + LD A,(BANK_M) ; #5B5C. + XOR #10 ; Select other ROM. + DI ; Disable interrupts whilst switching ROMs. + LD (BANK_M),A ; #5B5C. + OUT (C),A ; Switch to the other ROM. + EI ; + POP BC ; Restore BC and AF. + POP AF ; + RET ; +.Size EQU $-L006B +; --------------------------- +; Return to Other ROM Routine (copied to #5B14) +; --------------------------- +; Switch to the other ROM from that currently paged in +; and then return to the address held in RETADDR. + +;YOUNGER +L007F: CALL SWAP_ROM ; #5B00. Toggle to the other ROM. + PUSH HL ; + LD HL,(RETADDR) ; #5B5A. + EX (SP),HL ; + RET ; Return to the address held in RETADDR. + +; --------------------- +; Error Handler Routine (copied to #5B1D) +; --------------------- +; This error handler routine switches back to ROM 0 and then +; executes the routine pointed to by system variable TARGET. + +;ONERR +L0088: DI ; Ensure interrupts are disabled whilst paging. + LD A,(BANK_M) ; #5B5C. Fetch current paging configuration. + AND #EF ; Select ROM 0. + LD (BANK_M),A ; #5B5C. Save the new configuration + LD BC,#7FFD ; + OUT (C),A ; Switch to ROM 0. + EI ; + JP L00C3 ; Jump to #00C3 (ROM 0) to continue. + +; ------------------------- +; 'P' Channel Input Routine (copied to #5B2F) +; ------------------------- +; Called when data is read from channel 'P'. +; It causes ROM 0 to be paged in so that the new RS232 routines +; can be accessed. + +;PIN +L009A: LD HL,L06D8 ; RS232 input routine within ROM 0. + JR L00A2 ; + +; -------------------------- +; 'P' Channel Output Routine (copied to #5B34) +; -------------------------- +; Called when data is written to channel 'P'. +; It causes ROM 0 to be paged in so that the new RS232 routines +; can be accessed. +; Entry: A=Byte to send. + +;POUT +L009F: LD HL,L07CA ; RS232 output routine within ROM 0. + +L00A2: EX AF,AF' ; Save AF registers. + LD BC,#7FFD ; + LD A,(BANK_M) ; #5B5C. Fetch the current paging configuration + PUSH AF ; and save it. + AND #EF ; Select ROM 0. + DI ; Ensure interrupts are disabled whilst paging. + LD (BANK_M),A ; #5B5C. Store the new paging configuration. + OUT (C),A ; Switch to ROM 0. + JP L05E6 ; Jump to the RS232 channel input/output handler routine. + +; ------------------------ +; 'P' Channel Exit Routine (copied to #5B4A) +; ------------------------ +; Used when returning from a channel 'P' read or write operation. +; It causes the original ROM to be paged back in and returns back to +; the calling routine. + +;POUT2 +L00B5: EX AF,AF' ; Save AF registers. For a read, A holds the byte read and the flags the success status. + POP AF ; Retrieve original paging configuration. + LD BC,#7FFD ; + DI ; Ensure interrupts are disabled whilst paging. + LD (BANK_M),A ; #5B5C. Store original paging configuration. + OUT (C),A ; Switch back to original paging configuration. + EI ; + EX AF,AF' ; Restore AF registers. For a read, A holds the byte read and the flags the success status. + RET ; <<< End of RAM Routines >>> + + +; =============================== +; ERROR HANDLER ROUTINES - PART 2 +; =============================== + +; --------------- +; Call Subroutine +; --------------- +; Called from ONERR (#5B1D) to execute the routine pointed +; to by system variable SYNRET. + +L00C3: LD HL,(SYNRET) ; #5B8B. Fetch the address to call. + JP (HL) ; and execute it. + + +; ================================ +; INITIALISATION ROUTINES - PART 1 +; ================================ + +; -------------------------------------------- +; Reset Routine (RST #00 Continuation, Part 1) +; -------------------------------------------- +; Continuation from routine at #0000 (ROM 0). It performs a test on all RAM banks. +; This test is crude and can fail to detect a variety of RAM errors. + +L00C7: LD B,#08 ; Loop through all RAM banks. + +L00C9: LD A,B ; + EXX ; Save B register. + DEC A ; RAM bank number 0 to 7. 128K mode, ROM 0, Screen 0. + LD BC,#7FFD ; + OUT (C),A ; Switch RAM bank. + + LD HL,#C000 ; Start of the current RAM bank. + LD DE,#C001 ; + LD BC,#3FFF ; All 16K of RAM bank. + LD A,#FF ; + LD (HL),A ; Store #FF into RAM location. + CP (HL) ; Check RAM integrity. + JR NZ,L0131 ; Jump if RAM error found. + + XOR A ; + LD (HL),A ; Store #00 into RAM location. + CP (HL) ; Check RAM integrity. + JR NZ,L0131 ; Jump if difference found. + + LDIR ; Clear the whole page + EXX ; Restore B registers. + DJNZ L00C9 ; Repeat for other RAM banks. + + LD (ROW01),A ; #5B88. Signal no communications in progress to the keypad. + + LD C,#FD ; + LD D,#FF ; + LD E,#BF ; + LD B,D ; BC=#FFFD, DE=#FFBF. + LD A,#0E ; + OUT (C),A ; Select AY register 14. + LD B,E ; BC=#BFFD. + LD A,#FF ; + OUT (C),A ; Set AY register 14 to #FF. This will force a communications reset to the keypad if present. + JR L0137 ; Jump ahead to continue. + +L00FF: DEFB #00 ; [Spare byte] + + +; ==================== +; ROUTINE VECTOR TABLE +; ==================== + +L0100: JP L17AF ; BASIC interpreter parser. +L0103: JP L1838 ; 'Line Run' entry point. +L0106: JP L1ECF ; Transfer bytes to logical RAM bank 4. +L0109: JP L1F04 ; Transfer bytes from logical RAM bank 4. +L010C: JP L004A ; 128K error routine. +L010F: JP L03A2 ; Error routine. Called from patch at #3B3B in ROM 1. +L0112: JP L182A ; 'Statement Return' routine. Called from patch at #3B4D in ROM 1. +L0115: JP L18A8 ; 'Statement Next' routine. Called from patch at #3B5D in ROM 1. +L0118: JP L012D ; Scan the keypad. +L011B: JP L0A05 ; Play music strings. +L011E: JP L11A3 ; MIDI byte output routine. +L0121: JP L06D8 ; RS232 byte input routine. +L0124: JP L07CA ; RS232 text output routine. +L0127: JP L08A3 ; RS232 byte output routine. +L012A: JP L08F0 ; COPY (screen dump) routine. +L012D: RST #28 ; Call keypad scan routine in ROM 1. + DEFW KP_SCAN-#0100 ; #3B01. [*BUG* - The address jumps into the middle of the keypad decode routine in ROM 1. It + RET ; looks like it is supposed to deal with the keypad and so the most likely + ; addresses are #3A42 (read keypad) or #39A0 (scan keypad). At #3C01 in + ; ROM 1 is a vector jump command to #39A0 to scan the keypad and this is + ; similar enough to the #3B01 to imply a simple error in one of the bytes. Credit: Paul Farrow] + + +; ================================ +; INITIALISATION ROUTINES - PART 2 +; ================================ + +; --------------- +; Fatal RAM Error +; --------------- +; Set the border colour to indicate which RAM bank was found faulty: +; RAM bank 7 - Black. +; RAM bank 6 - White. +; RAM bank 5 - Yellow. +; RAM bank 4 - Cyan. +; RAM bank 3 - Green. +; RAM bank 2 - Magenta. +; RAM bank 1 - Red. +; RAM bank 0 - Blue. + +L0131: EXX ; Retrieve RAM bank number + 1 in B. + LD A,B ; Indicate which RAM bank failed by + OUT (#FE),A ; setting the border colour. + +L0135: JR L0135 ; Infinite loop. + +; -------------------------------------------- +; Reset Routine (RST #00 Continuation, Part 2) +; -------------------------------------------- +; Continuation from routine at #00C7 (ROM 0). + +L0137: LD B,D ; Complete setting up the sound chip registers. + LD A,#07 ; + OUT (C),A ; Select AY register 7. + LD B,E ; + LD A,#FF ; Disable AY-3-8912 sound channels. + OUT (C),A ; + + LD DE,SWAP_ROM ; #5B00. Copy the various paging routines to the old printer buffer. + LD HL,L006B ; The source is in this ROM. + LD BC,L006B.Size; There are eighty eight bytes to copy. + LDIR ; Copy the block of bytes. + + LD A,#CF ; Load A with the code for the Z80 instruction 'RST #08'. + LD (RAMRST),A ; #5B5D. Insert into new System Variable RAMRST. + LD SP,TSTACK ; #5BFF. Set the stack pointer to last location of old buffer. + + LD A,#04 ; + CALL L1C64 ; Page in logical RAM bank 4 (physical RAM bank 7). + + LD IX,#EBEC ; First free entry in RAM disk. + LD (SFNEXT),IX ; #5B83. + LD (IX+#0A),#00 ; + LD (IX+#0B),#C0 ; + LD (IX+#0C),#00 ; + LD HL,#2BEC ; + LD A,#01 ; AHL=Free space in RAM disk. + LD (SFSPACE),HL ; #5B85. Current address. + LD (SFSPACE+2),A ; #5B87. Current RAM bank. + + LD A,#05 ; + CALL L1C64 ; Page in logical RAM bank 5 (physical RAM bank 0). + + LD HL,#FFFF ; Load HL with known last working byte - 65535. + LD (#5CB4),HL ; P_RAMT. Set physical RAM top to 65535. + + LD DE,CHAR_SET+#01AF ; #3EAF. Set DE to address of the last bitmap of 'U' in ROM 1. + LD BC,#00A8 ; There are 21 User Defined Graphics to copy. + EX DE,HL ; Swap so destination is #FFFF. + RST #28 ; + DEFW MAKE_ROOM+#000C ; Calling this address (LDDR/RET) in the main ROM + ; cleverly copies the 21 characters to the end of RAM. + + EX DE,HL ; Transfer DE to HL. + INC HL ; Increment to address first byte of UDG 'A'. + LD (#5C7B),HL ; UDG. Update standard System Variable UDG. + + DEC HL ; + LD BC,#0040 ; Set values 0 for PIP and 64 for RASP. + LD (#5C38),BC ; RASP. Update standard System Variables RASP and PIP. + LD (#5CB2),HL ; RAMTOP. Update standard System Variable RAMTOP - the last + ; byte of the BASIC system area. Any machine code and + ; graphics above this address are protected from NEW. + +; Entry point for NEW with interrupts disabled and physical RAM bank 0 occupying +; the upper RAM region #C000 - #FFFF, i.e. the normal BASIC memory configuration. + +L019D: LD HL,CHAR_SET-#0100 ; #3C00. Set HL to where, in theory character zero would be. + LD (#5C36),HL ; CHARS. Update standard System Variable CHARS. + + LD HL,(#5CB2) ; RAMTOP. Load HL with value of System Variable RAMTOP. + INC HL ; Address next location. + LD SP,HL ; Set the Stack Pointer. + IM 1 ; Select Interrupt Mode 1. + LD IY,#5C3A ; Set the IY register to address the standard System + ; Variables and many of the new System Variables and + ; even those of ZX Interface 1 in some cases. + SET 4,(IY+#01) ; FLAGS. Signal 128K mode. + ; [This bit was unused and therefore never set by 48K BASIC] + + EI ; With a stack and the IY register set, interrupts can + ; be enabled. + + LD HL,#000B ; Set HL to eleven, timing constant for 9600 baud. + LD (BAUD),HL ; #5B5F. Select default RS232 baud rate of 9600 baud. + + XOR A ; Clear accumulator. + LD (SERFL),A ; #5B61. Indicate no byte waiting in RS232 receive buffer. + LD (COL),A ; #5B63. Set RS232 output column position to 0. + LD (TVPARS),A ; #5B65. Indicate no control code parameters expected. + + LD HL,#EC00 ; [*BUG* - Should write to RAM bank 7. Main RAM has now been corrupted. The value stored is subsequently never used. Credit: Geoff Wearmouth] + LD (#FF24),HL ; This is a remnant from the Spanish 128, which used this workspace variable to hold the location of the Screen Buffer, but + ; it also suffered from this bug. In fact there was never a need to write to the value at this point since it is written again later + ; during the initialisation process. + ; [The 1985 Sinclair Research ESPAGNOL source code says that this instruction will write to the (previously cleared) + ; main BASIC RAM during initialization but that a different page of RAM will be present during NEW. + ; Stuff and Nonsense! Assemblers and other utilities present above RAMTOP will be corrupted by the BASIC NEW command + ; since #FF24, and later #EC13, will be written to even if they are above RAMTOP.] + + LD A,#50 ; Default to a printer width of 80 columns. + LD (WIDTH),A ; #5B64. Set RS232 printer output width. + + LD HL,#000A ; Use 10 as the initial renumber line and increment. + LD (RNFIRST),HL ; #5B94. Store the initial line number when renumbering. + LD (RNSTEP),HL ; #5B96. Store the renumber line increment. + + LD HL,#5CB6 ; Address after the System Variables. + LD (#5C4F),HL ; CHANS. Set the default location for the channel area. + + LD DE,L0589 ; Point to Initial Channel Information in this ROM. + ; This is similar to that in main ROM but + ; channel 'P' has input and output addresses in the + ; new #5Bxx region. + LD BC,#0015 ; There are 21 bytes to copy. + EX DE,HL ; Switch pointer so destination is CHANS. + LDIR ; Copy the block of bytes. + + EX DE,HL ; + DEC HL ; Decrement to point to channel information end-marker. + LD (#5C57),HL ; DATADD. Set the default address of the terminator for the last DATA item. + + INC HL ; + LD (#5C53),HL ; PROG. Set the default address of the BASIC program area. + LD (#5C4B),HL ; VARS. Set the default address of the BASIC variables area. + LD (HL),#80 ; Insert the Variables end-marker. + + INC HL ; + LD (#5C59),HL ; E_LINE. Set the default address of the editing line area. + LD (HL),#0D ; Insert a carriage return. + INC HL ; + LD (HL),#80 ; Insert the editing line end-marker. + + INC HL ; + LD (#5C61),HL ; WORKSP. Set the address of the workspace. + LD (#5C63),HL ; STKBOT. Set the address of the start of the calculator stack. + LD (#5C65),HL ; STKEND. Set the address of the end of the calculator stack. + + LD A,#38 ; Attribute colour of black ink on white paper. + LD (#5C8D),A ; ATTR_P. Set the permanent attribute colour. + LD (#5C8F),A ; MASK_P. Set the permanent attribute mask. + LD (#5C48),A ; BORDCR. Set the default border colour. + + XOR A ; + LD (#EC13),A ; Temporary P_FLAG. Clear the temporary store for P-FLAG. + ; [*BUG* - Should write this to RAM bank 7. Main RAM has now been corrupted again. The effect + ; of the bug can be seen by typing INVERSE 1: PRINT "Hello", followed by NEW, followed by + ; PRINT "World", and will cause the second word to also be printed in inverse. Credit: Geoff Wearmouth] + + LD A,#07 ; + OUT (#FE),A ; Set the border white. + + LD HL,#0523 ; The values five and thirty five. + LD (#5C09),HL ; REPDEL. Set the default values for key delay and key repeat. + + DEC (IY-#3A) ; Set KSTATE+0 to #FF. + DEC (IY-#36) ; Set KSTATE+4 to #FF. + + LD HL,L059E ; Address of the Initial Stream Data within this ROM (which is identical to that in main ROM). + LD DE,#5C10 ; STRMS. Address of the system variable holding the channels attached to streams data. + LD BC,#000E ; + LDIR ; Initialise the streams system variables. + + RES 1,(IY+#01) ; FLAGS. Signal printer not is use. + LD (IY+#00),#FF ; ERR_NR. Signal no error. + LD (IY+#31),#02 ; DF_SZ. Set the lower screen size to two rows. + + RST #28 ; + DEFW CLS ; #0D6B. Clear the screen. + RST #28 ; Attempt to display TV tuning test screen. + DEFW TEST_SCREEN ; #3C04. Will return if BREAK is not being pressed. + + LD DE,L0561 ; Address of the Sinclair copyright message. + CALL L057D ; Display the copyright message. + + LD (IY+#31),#02 ; DF_SZ. Set the lower screen size to two rows. + SET 5,(IY+#02) ; TV_FLAG. Signal lower screen will require clearing. + + LD HL,TSTACK ; #5BFF. + LD (OLDSP),HL ; #5B81. Use the temporary stack as the previous stack. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD A,#38 ; Set colours to black ink on white paper. + LD (#EC11),A ; Temporary ATTR_T used by the 128 BASIC Editor. + LD (#EC0F),A ; Temporary ATTR_P used by the 128 BASIC Editor. + +; [Note this is where #EC13 (temporary P_FLAG) and #FF24 should be set] + + CALL L2584 ; Initialise mode and cursor settings. IX will point at editing settings information. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + JP L259F ; Jump to show the Main menu. + + +; =================================== +; COMMAND EXECUTION ROUTINES - PART 1 +; =================================== + +; -------------------- +; Execute Command Line +; -------------------- +; A typed in command resides in the editing workspace. Execute it. +; The command could either be a new line to insert, or a line number to delete, or a numerical expression to evaluate. + +L026B: LD HL,FLAGS3 ; #5B66. + SET 0,(HL) ; Select BASIC/Calculator mode. + + LD (IY+#00),#FF ; ERR_NR. Set to '0 OK' status. + LD (IY+#31),#02 ; DF_SZ. Reset the number of rows in the lower screen. + + LD HL,ONERR ; #5B1D. Return address should an error occur. + PUSH HL ; Stack it. + + LD (#5C3D),SP ; Save the stack pointer in ERR_SP. + + LD HL,L02BA ; Return address in ROM 0 after syntax checking. + LD (SYNRET),HL ; #5B8B. Store it in SYNRET. + + CALL L228E ; Point to start of typed in BASIC command. + CALL L22CB ; Is the first character a function token, i.e. the start of a numerical expression? + JP Z,L21F8 ; Jump if so to evaluate it. + + CP '(' ; #28. Is the first character the start of an expression? + JP Z,L21F8 ; Jump if so to evaluate it. + + CP '-' ; #2D. Is the first character the start of an expression? + JP Z,L21F8 ; Jump if so to evaluate it. + + CP '+' ; #2B. Is the first character the start of an expression? + JP Z,L21F8 ; Jump if so to evaluate it. + + CALL L22E0 ; Is text just a number or a numerical expression? + JP Z,L21F8 ; Jump if a numerical expression to evaluate it. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + LD A,(#EC0E) ; Fetch mode. + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + CP #04 ; Calculator mode? + JP NZ,L17AF ; Jump if not to parse and execute the BASIC command line, returning to #02BA (ROM 0). + +;Calculator mode + + CALL L2297 ; Is it a single LET command? + JP Z,L17AF ; Jump if so to parse and execute the BASIC command line, returning to #02BA (ROM 0). + +;Otherwise ignore the command + + POP HL ; Drop ONERR return address. + RET ; + +; ----------------------------------- +; Return from BASIC Line Syntax Check +; ----------------------------------- +; This routine is returned to when a BASIC line has been syntax checked. + +L02BA: BIT 7,(IY+#00) ; Test ERR_NR. + JR NZ,L02C1 ; Jump ahead if no error. + + RET ; Simply return if an error. + +;The syntax check was successful, so now proceed to parse the line for insertion or execution + +L02C1: LD HL,(#5C59) ; ELINE. Point to start of editing area. + LD (#5C5D),HL ; Store in CH_ADD. + RST #28 ; + DEFW E_LINE_NO ; #19FB. Call E_LINE_NO in ROM 1 to read the line number into editing area. + LD A,B ; + OR C ; + JP NZ,L03F7 ; Jump ahead if there was a line number. + +; -------------------------------------- +; Parse a BASIC Line with No Line Number +; -------------------------------------- + + RST #18 ; Get character. + CP #0D ; End of the line reached, i.e. no BASIC statement? + RET Z ; Return if so. + + CALL L21EF ; Clear screen if it requires it. + + BIT 6,(IY+#02) ; TVFLAG. Clear lower screen? + JR NZ,L02DF ; Jump ahead if no need to clear lower screen. + + RST #28 ; + DEFW CLS_LOWER ; #0D6E. Clear the lower screen. + +L02DF: RES 6,(IY+#02) ; TVFLAG. Signal to clear lower screen. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD HL,#EC0D ; Editor flags. + BIT 6,(HL) ; Using lower screen area for editing? + JR NZ,L02F4 ; Jump ahead if so. + + INC HL ; + LD A,(HL) ; Fetch the mode. + CP #00 ; In Edit Menu mode? + CALL Z,L3881 ; If so then clear lower editing area display. + +L02F4: CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + LD HL,#5C3C ; TVFLAG. + RES 3,(HL) ; Signal mode has not changed. + + LD A,#19 ; 25. + SUB (IY+#4F) ; S_POSN+1. Subtract the current print row position. + LD (#5C8C),A ; SCR_CT. Set the number of scrolls. + + SET 7,(IY+#01) ; FLAGS. Not syntax checking. + + LD (IY+#0A),#01 ; NSPPC. Set line to be jumped to as line 1. + +; [*BUG* - Whenever a typed in command is executed directly from the editing workspace, a new GO SUB marker is set up on the +; stack. Any existing GO SUB calls that were on the stack are lost and as a result attempting to continue the program +; (without the use of CLEAR or RUN) will likely lead to a "7 RETURN without GOSUB" error report message being displayed. +; However, the stack marker will already have been lost due to the error handler routine at #0321. The first action it +; does is to reset the stack pointer to point to the location of RAMTOP, i.e. after the GO SUB marker. This is why it is +; necessary for a new GO SUB marker needs to be set up. Credit: Michal Skrzypek] + + LD HL,#3E00 ; The end of GO SUB stack marker. + PUSH HL ; Place it on the stack. + + LD HL,ONERR ; #5B1D. The return address should an error occur. + PUSH HL ; Place it on the stack. + + LD (#5C3D),SP ; ERR_SP. Store error routine address. + + LD HL,L0321 ; Address of error handler routine in ROM 0. + LD (SYNRET),HL ; #5B8B. Store it in SYNRET. + + JP L1838 ; Jump ahead to the main parser routine to execute the line. + + +; =============================== +; ERROR HANDLER ROUTINES - PART 3 +; =============================== + +; --------------------- +; Error Handler Routine +; --------------------- + +; [*BUG* - Upon terminating a BASIC program, either via reaching the end of the program or due to an error occurring, +; execution is passed to this routine. The first action it does is to reset the stack pointer to point to the +; location of RAMTOP, i.e. after the GO SUB marker. However, this means that any existing GO SUB calls that +; were on the stack are lost and so attempting to continue the program (without the use of CLEAR or RUN) will +; likely lead to a "7 RETURN without GOSUB" error report message being displayed. When a new typed in command +; is executed, the code at #030C sets up a new GO SUB marker on the stack. Credit: Michal Skrzypek] + +L0321: LD SP,(#5CB2) ; RAMTOP. + INC SP ; Reset SP to top of memory map. + + LD HL,TSTACK ; #5BFF. + LD (OLDSP),HL ; #5B81. Use the temporary stack as the previous stack. + + HALT ; Trap error conditions where interrupts are disabled. + + RES 5,(IY+#01) ; FLAGS. Signal no new key. + LD HL,FLAGS3 ; #5B66. + BIT 2,(HL) ; Editing RAM disk catalogue? + JR Z,L034A ; Jump if not. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD IX,(SFNEXT) ; #5B83. + LD BC,#0014 ; Catalogue entry size. + ADD IX,BC ; Remove last entry. + CALL L1D56 ; Update catalogue entry (leaves logical RAM bank 4 paged in). + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + +;Display error code held in ERR_NR + +L034A: LD A,(#5C3A) ; Fetch error number from ERR_NR. + INC A ; Increment to give true error code. + +L034E: PUSH AF ; Save the error code. + + LD HL,#0000 ; + LD (IY+#37),H ; FLAGX. Ensure not INPUT mode. + LD (IY+#26),H ; X_PTR_hi. Clear to suppress error '?' marker. + LD (#5C0B),HL ; DEFADD. Clear to signal no defined function is currently being evaluated. + + LD HL,#0001 ; [Could have saved 2 bytes by using INC L]. + LD (#5C16),HL ; STRMS+#0006. Ensure STRMS-00 specifies the keyboard. + + RST #28 ; + DEFW SET_MIN ; #16B0. Clears editing area and areas after it. + RES 5,(IY+#37) ; FLAGX. Signal not INPUT mode. [Redundant since all flags were reset earlier] + + RST #28 ; + DEFW CLS_LOWER ; #0D6E. Clear lower editing screen. + SET 5,(IY+#02) ; TVFLAG. Signal lower screen requires clearing. + + POP AF ; Retrieve error code. + LD B,A ; Store error code in B. + CP #0A ; Is it a numeric error code (1-9), i.e. suitable for immediate display? + JR C,L037F ; If so jump ahead to display it. + + CP #1D ; Is it one of the standard errors (A-R)? + JR C,L037D ; If so jump ahead to convert it into an upper case letter. + + ADD A,#14 ; Otherwise convert it into a lower case letter. + JR L037F ; Jump ahead to display it. + ; [Could have saved 2 bytes by using ADD A,#0C instead of these two instructions] + +L037D: ADD A,#07 ; Increase code to point to upper case letters. + +L037F: RST #28 ; + DEFW OUT_CODE ; #15EF. Display the character held in the A register. + + LD A,#20 ; Display a space. + RST #10 ; + LD A,B ; Retrieve the error code. + CP #1D ; Is it one of the standard errors (A-R)? + JR C,L039C ; Jump if an standard error message (A-R). + +;Display a new error message + +; [Note that there is no test to range check the error code value and therefore whether a message exists for it. +; Poking directly to system variable ERR_NR with an invalid code (43 or above) will more than likely cause a crash] + + SUB #1D ; A=Code #00 - #0E. + LD B,#00 ; + LD C,A ; Pass code to BC. + LD HL,L046C ; Error message vector table. + ADD HL,BC ; + ADD HL,BC ; Find address in error message vector table. + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Address of message to print. + + CALL L057D ; Print error message. + JR L03A2 ; Jump ahead. + +;Display a standard error message. + +L039C: LD DE,ERROR_MSGS ; #1391. Position of the error messages in ROM 1. + RST #28 ; A holds the error code. + DEFW PO_MSG ; #0C0A. Call message printing routine. + +;Continue to display the line and statement number + +L03A2: XOR A ; Select the first message ", " (a 'comma' and a 'space'). + LD DE,MESSAGES-1 ; #1536. Message base address in ROM 1. + RST #28 ; + DEFW PO_MSG ; Print a comma followed by a space. + + LD BC,(#5C45) ; PPC. Fetch current line number. + RST #28 ; + DEFW OUT_NUM_1 ; #1A1B. Print the line number. + + LD A,#3A ; Print ':'. + RST #10 ; + + LD C,(IY+#0D) ; SUBPPC. Fetch current statement number. + LD B,#00 ; + RST #28 ; + DEFW OUT_NUM_1 ; #1A1B. Print the statement number. + + RST #28 ; + DEFW CLEAR_SP ; #1097. Clear editing and workspace areas. + + LD A,(#5C3A) ; ERR_NR. Fetch the error code. + INC A + JR Z,L03DF ; Jump ahead for "0 OK". + + CP #09 ; + JR Z,L03CC ; Jump for "A Invalid argument", thereby advancing to the next statement. + + CP #15 ; + JR NZ,L03CF ; Jump unless "M Ramtop no good". + +L03CC: INC (IY+#0D) ; SUBPPC. Advance to the next statement. + +L03CF: LD BC,#0003 ; + LD DE,#5C70 ; OSPPC. Continue statement number. + LD HL,#5C44 ; NSPPC. Next statement number. + BIT 7,(HL) ; Is there a statement number? + JR Z,L03DD ; Jump if so. + + ADD HL,BC ; HL=SUBPPC. The current statement number. + +L03DD: LDDR ; Copy SUBPPC and PPC to OSPPC and OLDPPC, for use by CONTINUE. + +L03DF: LD (IY+#0A),#FF ; NSPPC. Signal no current statement number. + RES 3,(IY+#01) ; FLAGS. Select K-Mode. + LD HL,FLAGS3 ; #5B66. + RES 0,(HL) ; Select 128 Editor mode. + JP L25CB ; Jump ahead to return control to the Editor. + +; --------------------------------------------- +; Error Handler Routine When Parsing BASIC Line +; --------------------------------------------- + +L03EF: LD A,#10 ; Error code 'G - No room for line'. + LD BC,#0000 ; + JP L034E ; Jump to print the error code. + + +; =================================== +; COMMAND EXECUTION ROUTINES - PART 2 +; =================================== + +; ------------------------------------- +; Parse a BASIC Line with a Line Number +; ------------------------------------- +; This routine handles insertion of a BASIC line specified with a line number, or just a line number +; specified on its own, i.e. delete the line. + +L03F7: LD (#5C49),BC ; E_PPC. Store the line as the current line number with the program cursor. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD A,B ; [This test could have been performed before paging in bank 7 and hence could have benefited from a slight speed improvement. + OR C ; The test is redundant since BC holds a non-zero line number] + JR Z,L040A ; Jump if no line number. + + LD (#5C49),BC ; E_PPC. Current edit line number. [Redundant instruction - Line number has already been stored] + LD (#EC08),BC ; Temporary E_PPC used by BASIC Editor. + +L040A: CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + LD HL,(#5C5D) ; CH_ADD. Point to the next character in the BASIC line. + EX DE,HL ; + + LD HL,L03EF ; Address of error handler routine should there be no room for the line. + PUSH HL ; Stack it. + + LD HL,(#5C61) ; WORKSP. + SCF ; + SBC HL,DE ; HL=Length of BASIC line. + PUSH HL ; Stack it. + + LD H,B ; + LD L,C ; Transfer edit line number to HL. + RST #28 ; + DEFW LINE_ADDR ; #196E. Returns address of the line in HL. + JR NZ,L0429 ; Jump if the line does not exist. + +;The line already exists so delete it + + RST #28 ; + DEFW NEXT_ONE ; #19B8. Find the address of the next line. + RST #28 ; + DEFW RECLAIM_2 ; #19E8. Delete the line. + +L0429: POP BC ; BC=Length of the BASIC line. + LD A,C ; + DEC A ; Is it 1, i.e. just an 'Enter' character, and hence only + OR B ; a line number was entered? + JR NZ,L0442 ; Jump if there is a BASIC statement. + +;Just a line number entered. The requested line has already been deleted so move the program cursor to the next line + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + PUSH HL ; Save the address of the line. + + LD HL,(#5C49) ; E_PPC. Fetch current edit line number. + CALL L334A ; Find closest line number (or #0000 if no line). + LD (#5C49),HL ; E_PPC. Store current edit line number. Effectively refresh E_PPC. + + POP HL ; HL=Address of the line. + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + JR L046A ; Jump ahead to exit. + +L0442: PUSH BC ; BC=Length of the BASIC line. Stack it. + + INC BC ; + INC BC ; + INC BC ; + INC BC ; BC=BC+4. Allow for line number and length bytes. + + DEC HL ; Point to before the current line, i.e. the location to insert bytes at. + + LD DE,(#5C53) ; PROG. Get start address of the BASIC program. + PUSH DE ; Stack it. + + RST #28 ; + DEFW MAKE_ROOM ; #1655. Insert BC spaces at address HL. + + POP HL ; HL=Start address of BASIC program. + LD (#5C53),HL ; PROG. Save start address of BASIC program. + + POP BC ; BC=Length of the BASIC line. + PUSH BC ; + + INC DE ; Point to the first location of the newly created space. + LD HL,(#5C61) ; WORKSP. Address of end of the BASIC line in the workspace. + DEC HL ; + DEC HL ; Skip over the newline and terminator bytes. + LDDR ; Copy the BASIC line from the workspace into the program area. + LD HL,(#5C49) ; E_PPC. Current edit line number. + EX DE,HL ; + + POP BC ; BC=Length of BASIC line. + LD (HL),B ; Store the line length. + DEC HL ; + LD (HL),C ; + DEC HL ; + LD (HL),E ; DE=line number. + DEC HL ; + LD (HL),D ; Store the line number. + +L046A: POP AF ; Drop item (address of error handler routine). + RET ; Exit with HL=Address of the line. + + +; =============================== +; ERROR HANDLER ROUTINES - PART 4 +; =============================== + +; ------------------------------ +; New Error Message Vector Table +; ------------------------------ +; Pointers into the new error message table. + +L046C: DEFW L048C ; Error report 'a'. + DEFW L0497 ; Error report 'b'. + DEFW L04A6 ; Error report 'c'. + DEFW L04B0 ; Error report 'd'. + DEFW L04C1 ; Error report 'e'. + DEFW L04D4 ; Error report 'f'. + DEFW L04E0 ; Error report 'g'. + DEFW L04E0 ; Error report 'h'. + DEFW L04F3 ; Error report 'i'. + DEFW L0501 ; Error report 'j'. + DEFW L0512 ; Error report 'k'. + DEFW L0523 ; Error report 'l' + DEFW L0531 ; Error report 'm'. + DEFW L0542 ; Error report 'n'. + DEFW L054E ; Error report 'o'. + DEFW L0561 ; Error report 'p'. + +; ----------------------- +; New Error Message Table +; ----------------------- + +L048C: DEFM "MERGE erro" ; Report 'a'. + DEFB 'r'+#80 +L0497: DEFM "Wrong file typ" ; Report 'b'. + DEFB 'e'+#80 +L04A6: DEFM "CODE erro" ; Report 'c'. + DEFB 'r'+#80 +L04B0: DEFM "Too many bracket" ; Report 'd'. + DEFB 's'+#80 +L04C1: DEFM "File already exist" ; Report 'e'. + DEFB 's'+#80 +L04D4: DEFM "Invalid nam" ; Report 'f'. + DEFB 'e'+#80 +L04E0: DEFM "File does not exis" ; Report 'g' & 'h'. + DEFB 't'+#80 +L04F3: DEFM "Invalid devic" ; Report 'i'. + DEFB 'e'+#80 +L0501: DEFM "Invalid baud rat" ; Report 'j'. + DEFB 'e'+#80 +L0512: DEFM "Invalid note nam" ; Report 'k'. + DEFB 'e'+#80 +L0523: DEFM "Number too bi" ; Report 'l'. + DEFB 'g'+#80 +L0531: DEFM "Note out of rang" ; Report 'm'. + DEFB 'e'+#80 +L0542: DEFM "Out of rang" ; Report 'n'. + DEFB 'e'+#80 +L054E: DEFM "Too many tied note" ; Report 'o'. + DEFB 's'+#80 +L0561: DEFB #7F ; '(c)'. + DEFM " 1986 Sinclair Research Lt" ; Copyright. [There should have been an error report "p Bad parameterr" here as there was in the Spanish 128, + DEFB 'd'+#80 ; or the error code byte at #232F (ROM 0) should have been #19 for "Q Parameter error"] + +; ------------- +; Print Message +; ------------- +; Print a message which is terminated by having bit 7 set, pointed at by DE. + +L057D: LD A,(DE) ; Fetch next byte. + AND #7F ; Mask off top bit. + PUSH DE ; Save address of current message byte. + RST #10 ; Print character. + POP DE ; Restore message byte pointer. + LD A,(DE) ; + INC DE ; + ADD A,A ; Carry flag will be set if byte is #FF. + JR NC,L057D ; Else print next character. + + RET ; + +; ================================ +; INITIALISATION ROUTINES - PART 3 +; ================================ + +; --------------------------------- +; The 'Initial Channel Information' +; --------------------------------- +; Initially there are four channels ('K', 'S', 'R', & 'P') for communicating with the 'keyboard', 'screen', 'work space' and 'printer'. +; For each channel the output routine address comes before the input routine address and the channel's code. +; This table is almost identical to that in ROM 1 at #15AF but with changes to the channel P routines to use the RS232 port +; instead of the ZX Printer. +; Used at #01DD (ROM 0). + +L0589: DEFW PRINT_OUT ; #09F4 - K channel output routine. + DEFW KEY_INPUT ; #10A8 - K channel input routine. + DEFB 'K' ; #4B - Channel identifier 'K'. + DEFW PRINT_OUT ; #09F4 - S channel output routine. + DEFW REPORT_J ; #15C4 - S channel input routine. + DEFB 'S' ; #53 - Channel identifier 'S'. + DEFW ADD_CHAR ; #0F81 - R channel output routine. + DEFW REPORT_J ; #15C4 - R channel input routine. + DEFB 'R' ; #52 - Channel identifier 'R'. + DEFW POUT ; #5B34 - P Channel output routine. + DEFW PIN ; #5B2F - P Channel input routine. + DEFB 'P' ; #50 - Channel identifier 'P'. + DEFB #80 ; End marker. + +; ------------------------- +; The 'Initial Stream Data' +; ------------------------- +; Initially there are seven streams - #FD to #03. +; This table is identical to that in ROM 1 at #15C6. +; Used at #0226 (ROM 0). + +L059E: DEFB #01, #00 ; Stream #FD leads to channel 'K'. + DEFB #06, #00 ; Stream #FE leads to channel 'S'. + DEFB #0B, #00 ; Stream #FF leads to channel 'R'. + DEFB #01, #00 ; Stream #00 leads to channel 'K'. + DEFB #01, #00 ; Stream #01 leads to channel 'K'. + DEFB #06, #00 ; Stream #02 leads to channel 'S'. + DEFB #10, #00 ; Stream #03 leads to channel 'P'. + + +; =============================== +; ERROR HANDLER ROUTINES - PART 5 +; =============================== + +; -------------------- +; Produce Error Report +; -------------------- + +L05AC: POP HL ; Point to the error byte. + LD BC,#7FFD ; + XOR A ; ROM 0, Screen 0, Bank 0, 128 mode. + DI ; Ensure interrupts disable whilst paging. + LD (BANK_M),A ; #5B5C. Store new state in BANK_M. + OUT (C),A ; Switch to ROM 0. + EI ; + + LD SP,(#5C3D) ; Restore SP from ERR_SP. + LD A,(HL) ; Fetch the error number. + LD (RAMERR),A ; #5B5E. Store the error number. + INC A ; + CP #1E ; [*BUG* - This should be #1D. As such, error code 'a' will be diverted to ROM 1 for handling. Credit: Paul Farrow] + JR NC,L05C8 ; Jump if not a standard error code. + +;Handle a standard error code + + RST #28 ; + DEFW RAMRST ; #5B5D. Call the error handler routine in ROM 1. + +;Handle a new error code + +L05C8: DEC A ; + LD (IY+#00),A ; Store in ERR_NR. + LD HL,(#5C5D) ; CH_ADD. + LD (#5C5F),HL ; X_PTR. Set up the address of the character after the '?' marker. + + RST #28 ; + DEFW SET_STK ; #16C5. Set the calculator stack. + RET ; Return to the error routine. + +; ---------------------------- +; Check for BREAK into Program +; ---------------------------- + +L05D6: LD A,#7F ; Read keyboard row B - SPACE. + IN A,(#FE) ; + RRA ; Extract the SPACE key. + RET C ; Return if SPACE not pressed. + + LD A,#FE ; Read keyboard row CAPS SHIFT - V. + IN A,(#FE) ; + RRA ; Extract the CAPS SHIFT key. + RET C ; Return if CAPS SHIFT not pressed. + + CALL L05AC ; Produce an error. + DEFB #14 ; "L Break into program" + + +; ====================== +; RS232 PRINTER ROUTINES +; ====================== + +; ------------------------------ +; RS232 Channel Handler Routines +; ------------------------------ +; This routine handles input and output RS232 requested. It is similar to the +; routine in the ZX Interface 1 ROM at #0D5A, but in that ROM the routine is only used +; for input. + +L05E6: EI ; Enabled interrupts. + EX AF,AF' ; Save AF registers. + LD DE,POUT2 ; #5B4A. Address of the RS232 exit routine held in RAM. + PUSH DE ; Stack it. + + RES 3,(IY+#02) ; TVFLAG. Indicate not automatic listing. + PUSH HL ; Save the input/output routine address. + + LD HL,(#5C3D) ; Fetch location of error handler routine from ERR_SP. + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Address of error handler routine. + AND A ; + LD HL,ED_ERROR ; #107F in ROM 1. + SBC HL,DE ; + JR NZ,L0637 ; Jump if error handler address is different, i.e. due to INKEY$# or PRINT#. + +; Handle INPUT# +; ------------- + + POP HL ; Retrieve the input/output routine address. + LD SP,(#5C3D) ; ERR_SP. + POP DE ; Discard the error handler routine address. + POP DE ; Fetch the original address of ERR_SP (this was stacked at the beginning of the INPUT routine in ROM 1). + LD (#5C3D),DE ; ERR_SP. + +L060A: PUSH HL ; Save the input/output routine address. + LD DE,L0610 ; Address to return to. + PUSH DE ; Stack the address. + JP (HL) ; Jump to the RS232 input/output routine. + +;Return here from the input/output routine + +L0610: JR C,L061B ; Jump if a character was received. + JR Z,L0618 ; Jump if a character was not received. + +L0614: CALL L05AC ; Produce an error "8 End of file". + DEFB #07 ; + +;A character was not received + +L0618: POP HL ; Retrieve the input routine address. + JR L060A ; Jump back to await another character. + +;A character was received + +L061B: CP #0D ; Is it a carriage return? + JR Z,L062D ; Jump ahead if so. + + LD HL,(RETADDR) ; #5B5A. Fetch the return address. + PUSH HL ; + + RST #28 ; + DEFW ADD_CHAR+4 ; #0F85. Insert the character into the INPUT line. + + POP HL ; + LD (RETADDR),HL ; #5B5A. Restore the return address. + + POP HL ; Retrieve the input routine address. + JR L060A ; Jump back to await another character. + +;Enter was received so end reading the stream + +L062D: POP HL ; Discard the input routine address. + LD A,(BANK_M) ; #5B5C. Fetch current paging configuration. + OR #10 ; Select ROM 1. + PUSH AF ; Stack the required paging configuration. + JP POUT2 ; #5B4A. Exit. + +; Handle INKEY$# and PRINT# +; ------------------------- + +L0637: POP HL ; Retrieve the input/output routine address. + LD DE,L063D ; + PUSH DE ; Stack the return address. + JP (HL) ; Jump to input or output routine. + +;Return here from the input/output routine. When returning from the output routine, either the carry or zero flags should always +;be set to avoid the false generation of error report "8 End of file" [though this is not always the case - see bugs starting at #086C (ROM 0)]. + +L063D: RET C ; Return if a character was received. + RET Z ; Return if a character was not received or was written. + + JR L0614 ; Produce error report "8 End of file". + +; -------------- +; FORMAT Routine +; -------------- +; The format command sets the RS232 baud rate, e.g. FORMAT "P"; 9600. +; It attempts to match against one of the supported baud rates, or uses the next +; higher baud rate if a non-standard value is requested. The maximum baud rate supported +; is 9600, and this is used for any rates specified that are higher than this. + +L0641: RST #28 ; [Could just do RST #18] + DEFW GET_CHAR ; #0018. + RST #28 ; Get an expression. + DEFW EXPT_EXP ; #1C8C. + BIT 7,(IY+#01) ; FLAGS. + JR Z,L0661 ; Jump ahead if syntax checking. + + RST #28 ; + DEFW STK_FETCH ; #2BF1. Fetch the expression. + LD A,C ; + DEC A ; + OR B ; + JR Z,L0659 ; Jump ahead if string is 1 character long. + + CALL L05AC ; Produce error report. + DEFB #24 ; "i Invalid device". + +L0659: LD A,(DE) ; Get character. + AND #DF ; Convert to upper case. + CP 'P' ; #50. Is it channel 'P'? + JP NZ,L1912 ; Jump if not to produce error report "C Nonsense in BASIC". + +L0661: LD HL,(#5C5D) ; CH_ADD. Next character to be interpreted. + LD A,(HL) ; + CP #3B ; Next character must be ';'. + JP NZ,L1912 ; Jump if not to produce error report "C Nonsense in BASIC". + + RST #28 ; Skip past the ';' character. + DEFW NEXT_CHAR ; #0020. [Could just do RST #20] + RST #28 ; Get a numeric expression from the line. + DEFW EXPT_1NUM ; #1C82. + BIT 7,(IY+#01) ; FLAGS. Checking syntax mode? + JR Z,L067D ; Jump ahead if so. + + RST #28 ; Get the result as an integer. + DEFW FIND_INT2 ; #1E99. + LD (HD_00),BC ; #5B71. Store the result temporarily for use later. + +L067D: RST #28 ; [Could just do RST #18] + DEFW GET_CHAR ; #0018. Get the next character in the BASIC line. + CP #0D ; It should be ENTER. + JR Z,L0689 ; Jump ahead if it is. + + CP ':' ; #3A. Or the character is allowed to be ':'. + JP NZ,L1912 ; Jump if not to produce error report "C Nonsense in BASIC". + +L0689: CALL L18A1 ; Check for end of line. + LD BC,(HD_00) ; #5B71. Get the baud rate saved earlier. + LD A,B ; Is it zero? + OR C ; + JR NZ,L0698 ; Jump if not, i.e. a numeric value was specified. + + CALL L05AC ; Produce error report. + DEFB #25 ; "j invalid baud rate" + +;Lookup the timing constant to use for the specified baud rate + +L0698: LD HL,L06B8 ; Table of supported baud rates. + +L069B: LD E,(HL) ; + INC HL ; + LD D,(HL) ; + INC HL ; + EX DE,HL ; HL=Supported baud rate value. + LD A,H ; + CP #25 ; Reached the last baud rate value in the table? + JR NC,L06AF ; Jump is so to use a default baud rate of 9600. + + AND A ; + SBC HL,BC ; Table entry matches or is higher than requested baud rate? + JR NC,L06AF ; Jump ahead if so to use this baud rate. + + EX DE,HL ; + INC HL ; Skip past the timing constant value + INC HL ; for this baud rate entry. + JR L069B ; + +;The baud rate has been matched + +L06AF: EX DE,HL ; HL points to timing value for the baud rate. + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Timing value for the baud rate. + LD (BAUD),DE ; #5B71. Store new value in system variable BAUD. + RET ; + +; --------------- +; Baud Rate Table +; --------------- +; Consists of entries of baud rate value followed by timing constant to use in the RS232 routines. + +L06B8: DEFW #0032, #0AA5 ; Baud=50. + DEFW #006E, #04D4 ; Baud=110. + DEFW #012C, #01C3 ; Baud=300. + DEFW #0258, #00E0 ; Baud=600. + DEFW #04B0, #006E ; Baud=1200. + DEFW #0960, #0036 ; Baud=2400. + DEFW #12C0, #0019 ; Baud=4800. + DEFW #2580, #000B ; Baud=9600. + +; ------------------- +; RS232 Input Routine +; ------------------- +; Exit: Carry flag set if a byte was read with the byte in A. Carry flag reset upon error. + +L06D8: +;[v]..................................................................................................................... SP2000 [v] +; LD HL,SERFL ; #5B61. SERFL holds second char that can be received + JP L08A5 +;[^]..................................................................................................................... SP2000 [^] + +;!FIXIT вот это теперь не используется и сюда можно своего воткнуть [v] + LD A,(HL) ; Is the second-character received flag set? + AND A ; i.e. have we already received data? + JR Z,L06E5 ; Jump ahead if not. + + LD (HL),#00 ; Otherwise clear the flag + INC HL ; + LD A,(HL) ; and return the data which we received earlier. + SCF ; Set carry flag to indicate success + RET ; +;- - - - - - - - - - - - - - - - - - - - - - - - [^] + +; ------------------------- +; Read Byte from RS232 Port +; ------------------------- +; The timing of the routine is achieved using the timing constant held in system variable BAUD. +; Exit: Carry flag set if a byte was read, or reset upon error. +; A=Byte read in. + +L06E5: CALL L05D6 ; Check the BREAK key, and produce error message if it is being pressed. + + DI ; Ensure interrupts are disabled to achieve accurate timing. + + EXX ; + + LD DE,(BAUD) ; #5B71. Fetch the baud rate timing constant. + LD HL,(BAUD) ; #5B71. + SRL H ; + RR L ; HL=BAUD/2. So that will sync to half way point in each bit. + + OR A ; [Redundant byte] + + LD B,#FA ; Waiting time for start bit. + EXX ; Save B. + LD C,#FD ; + LD D,#FF ; + LD E,#BF ; + LD B,D ; + LD A,#0E ; + OUT (C),A ; Selects register 14, port I/O of AY-3-8912. + + IN A,(C) ; Read the current state of the I/O lines. + OR #F0 ; %11110000. Default all input lines to 1. + AND #FB ; %11111011. Force CTS line to 0. + LD B,E ; B=#BF. + OUT (C),A ; Make CTS (Clear To Send) low to indicate ready to receive. + + LD H,A ; Store status of other I/O lines. + +;Look for the start bit + +L070E: LD B,D ; + IN A,(C) ; Read the input line. + AND #80 ; %10000000. Test TXD (input) line. + JR Z,L071E ; Jump if START BIT found. + +L0715: EXX ; Fetch timeout counter + DEC B ; and decrement it. + EXX ; Store it. + JR NZ,L070E ; Continue to wait for start bit if not timed out. + + XOR A ; Reset carry flag to indicate no byte read. + PUSH AF ; Save the failure flag. + JR L0757 ; Timed out waiting for START BIT. + +L071E: IN A,(C) ; Second test of START BIT - it should still be 0. + AND #80 ; Test TXD (input) line. + JR NZ,L0715 ; Jump back if it is no longer 0. + + IN A,(C) ; Third test of START BIT - it should still be 0. + AND #80 ; Test TXD (input) line. + JR NZ,L0715 ; Jump back if it is no longer 0. + +;A start bit has been found, so the 8 data bits are now read in. +;As each bit is read in, it is shifted into the msb of A. Bit 7 of A is preloaded with a 1 +;to represent the start bit and when this is shifted into the carry flag it signifies that 8 +;data bits have been read in. + + EXX ; + LD BC,#FFFD ; + LD A,#80 ; Preload A with the START BIT. It forms a shift counter used to count + EX AF,AF' ; the number of bits to read in. + +L0731: ADD HL,DE ; HL=1.5*(BAUD). + + NOP ; (4) Fine tune the following delay. + NOP ; + NOP ; + NOP ; + +;BD-DELAY +L0736: DEC HL ; (6) Delay for 26*BAUD. + LD A,H ; (4) + OR L ; (4) + JR NZ,L0736 ; (12) Jump back to until delay completed. + + IN A,(C) ; Read a bit. + AND #80 ; Test TXD (input) line. + JP Z,L074B ; Jump if a 0 received. + +;Received one 1 + + EX AF,AF' ; Fetch the bit counter. + SCF ; Set carry flag to indicate received a 1. + RRA ; Shift received bit into the byte (C->76543210->C). + JR C,L0754 ; Jump if START BIT has been shifted out indicating all data bits have been received. + + EX AF,AF' ; Save the bit counter. + JP L0731 ; Jump back to read the next bit. + +;Received one 0 + +L074B: EX AF,AF' ; Fetch the bit counter. + OR A ; Clear carry flag to indicate received a 0. + RRA ; Shift received bit into the byte (C->76543210->C). + JR C,L0754 ; Jump if START BIT has been shifted out indicating all data bits have been received. + + EX AF,AF' ; Save the bit counter. + JP L0731 ; Jump back to read next bit. + +;After looping 8 times to read the 8 data bits, the start bit in the bit counter will be shifted out +;and hence A will contain a received byte. + +L0754: SCF ; Signal success. + PUSH AF ; Push success flag. + EXX + +;The success and failure paths converge here + +L0757: LD A,H ; + OR #04 ; A=%1111x1xx. Force CTS line to 1. + + LD B,E ; B=#BF. + OUT (C),A ; Make CTS (Clear To Send) high to indicate not ready to receive. + EXX + + LD H,D ; + LD L,E ; HL=(BAUD). + LD BC,#0007 ; + OR A ; + SBC HL,BC ; HL=(BAUD)-7. + +L0766: DEC HL ; Delay for the stop bit. + LD A,H ; + OR L ; + JR NZ,L0766 ; Jump back until delay completed. + + LD BC,#FFFD ; HL will be #0000. + ADD HL,DE ; DE=(BAUD). + ADD HL,DE ; + ADD HL,DE ; HL=3*(BAUD). This is how long to wait for the next start bit. + +;The device at the other end of the cable may send a second byte even though +;CTS is low. So repeat the procedure to read another byte. + +L0771: IN A,(C) ; Read the input line. + AND #80 ; %10000000. Test TXD (input) line. + JR Z,L077F ; Jump if START BIT found. + + DEC HL ; Decrement timeout counter. + LD A,H ; + OR L ; + JR NZ,L0771 ; Jump back looping for a start bit until a timeout occurs. + +;No second byte incoming so return status of the first byte read attempt + + POP AF ; Return status of first byte read attempt - carry flag reset for no byte received or + EI ; carry flag set and A holds the received byte. + RET + +L077F: IN A,(C) ; Second test of START BIT - it should still be 0. + AND #80 ; Test TXD (input) line. + JR NZ,L0771 ; Jump back if it is no longer 0. + + IN A,(C) ; Third test of START BIT - it should still be 0. + AND #80 ; Test TXD (input) line. + JR NZ,L0771 ; Jump back if it is no longer 0. + +;A second byte is on its way and is received exactly as before + + LD H,D ; + LD L,E ; HL=(BAUD). + LD BC,#0002 ; + SRL H ; + RR L ; HL=(BAUD)/2. + OR A ; + SBC HL,BC ; HL=(BAUD)/2 - 2. + + LD BC,#FFFD ; + LD A,#80 ; Preload A with the START BIT. It forms a shift counter used to count + EX AF,AF' ; the number of bits to read in. + +L079D: NOP ; Fine tune the following delay. + NOP ; + NOP ; + NOP ; + + ADD HL,DE ; HL=1.5*(BAUD). + +L07A2: DEC HL ; Delay for 26*(BAUD). + LD A,H ; + OR L ; + JR NZ,L07A2 ; Jump back to until delay completed. + + IN A,(C) ; Read a bit. + AND #80 ; Test TXD (input) line. + JP Z,L07B7 ; Jump if a 0 received. + +;Received one 1 + + EX AF,AF' ; Fetch the bit counter. + SCF ; Set carry flag to indicate received a 1. + RRA ; Shift received bit into the byte (C->76543210->C). + JR C,L07C0 ; Jump if START BIT has been shifted out indicating all data bits have been received. + + EX AF,AF' ; Save the bit counter. + JP L079D ; Jump back to read the next bit. + +;Received one 0 + +L07B7: EX AF,AF' ; Fetch the bit counter. + OR A ; Clear carry flag to indicate received a 0. + RRA ; Shift received bit into the byte (C->76543210->C). + JR C,L07C0 ; Jump if START BIT has been shifted out indicating all data bits have been received. + + EX AF,AF' ; Save the bit counter. + JP L079D ; Jump back to read next bit. + +;Exit with the byte that was read in + +L07C0: LD HL,SERFL ; #5B61. + LD (HL),#01 ; Set the flag indicating a second byte is in the buffer. + INC HL ; + LD (HL),A ; Store the second byte read in the buffer. + POP AF ; Return the first byte. + + EI ; Re-enable interrupts. + RET + +; -------------------- +; RS232 Output Routine +; -------------------- +; This routine handles control codes, token expansion, graphics and UDGs. It therefore cannot send binary data and hence cannot support +; EPSON format ESC control codes [Credit: Andrew Owen]. +; The routine suffers from a number of bugs as described in the comments below. It also suffers from a minor flaw in the design, which prevents +; interlacing screen and printer control codes and their parameters. For example, the following will not work correctly: +; +; 10 LPRINT CHR$ 16; +; 20 PRINT AT 0,0; +; 30 LPRINT CHR$ 0;"ABC" +; +; The control byte 16 gets stored in TVDATA so that the system knows how to interpret its parameter byte. However, the AT control code 22 +; in line 20 will overwrite it. When line 30 is executed, TVDATA still holds the control code for 'AT' and so this line is interpreted as +; PRINT AT instead of PRINT INK. [Credit: Ian Collier (+3)] +; +; Entry: A=character to output. +; Exit : Carry flag reset indicates success. + +L07CA: PUSH AF ; Save the character to print. + LD A,(TVPARS) ; #5B65. Number of parameters expected. + OR A ; + JR Z,L07E0 ; Jump if no parameters. + + DEC A ; Ignore the parameter. + LD (TVPARS),A ; #5B65. + JR NZ,L07DB ; Jump ahead if we have not processed all parameters. + +;All parameters processed + + POP AF ; Retrieve character to print. + JP L0872 ; Jump ahead to continue. + +L07DB: POP AF ; Retrieve character to print. + LD (#5C0F),A ; TVDATA+1. Store it for use later. + RET ; + +L07E0: POP AF ; Retrieve character to print. + CP #A3 ; Test against code for 'SPECTRUM'. + JR C,L07F2 ; Jump ahead if not a token. + +;Process tokens + + LD HL,(RETADDR) ; #5B5A. Save RETADDR temporarily. + PUSH HL ; + RST #28 ; + DEFW PO_T_UDG ; #0B52. Print tokens via call to ROM 1 routine PO-T&UDG. + POP HL ; + LD (RETADDR),HL ; #5B5A. Restore the original contents of RETADDR. + SCF ; + RET ; + +L07F2: LD HL,#5C3B ; FLAGS. + RES 0,(HL) ; Suppress printing a leading space. + CP ' ' ; #20. Is character to output a space? + JR NZ,L07FD ; Jump ahead if not a space. + + SET 0,(HL) ; Signal leading space required. + +L07FD: CP #7F ; Compare against copyright symbol. + JR C,L0803 ; Jump ahead if not a graphic or UDG character. + + LD A,'?' ; #3F. Print a '?' for all graphic and UDG characters. + +L0803: CP #20 ; Is it a control character? + JR C,L081E ; Jump ahead if so. + +;Printable character + +L0807: PUSH AF ; Save the character to print. + LD HL,COL ; #5B63. Point to the column number. + INC (HL) ; Increment the column number. + LD A,(WIDTH) ; #5B64. Fetch the number of columns. + CP (HL) ; + JR NC,L081A ; Jump if end of row not reached. + + CALL L0822 ; Print a carriage return and line feed. + + LD A,#01 ; + LD (COL),A ; #5B63. Set the print position to column 1. + +L081A: POP AF ; Retrieve character to print. + JP L08A3 ; Jump ahead to print the character. + +;Process control codes + +L081E: CP #0D ; Is it a carriage return? + JR NZ,L0830 ; Jump ahead if not. + +;Handle a carriage return + +L0822: XOR A ; + LD (COL),A ; #5B63. Set the print position back to column 0. + + LD A,#0D ; + CALL L08A3 ; Print a carriage return. + + LD A,#0A ; + JP L08A3 ; Print a line feed. + +L0830: CP #06 ; Is it a comma? + JR NZ,L0853 ; Jump ahead if not. + +;Handle a comma + + LD BC,(COL) ; #5B63. Fetch the column position. + LD E,#00 ; Will count number of columns to move across to reach next comma position. + +L083A: INC E ; Increment column counter. + INC C ; Increment column position. + LD A,C ; + CP B ; End of row reached? + JR Z,L0848 ; Jump if so. + +L0840: SUB #08 ; + JR Z,L0848 ; Jump if column 8, 16 or 32 reached. + + JR NC,L0840 ; Column position greater so subtract another 8. + + JR L083A ; Jump back and increment column position again. + +;Column 8, 16 or 32 reached. Output multiple spaces until the desired column position is reached. + +L0848: PUSH DE ; Save column counter in E. + LD A,#20 ; + CALL L07CA ; Output a space via a recursive call. + POP DE ; Retrieve column counter to E. + DEC E ; More spaces to output? + RET Z ; Return if no more to output. + + JR L0848 ; Repeat for the next space to output. + +L0853: CP #16 ; Is it AT? + JR Z,L0860 ; Jump ahead to handle AT. + + CP #17 ; Is it TAB? + JR Z,L0860 ; Jump ahead to handle TAB. + + CP #10 ; Check for INK, PAPER, FLASH, BRIGHT, INVERSE, OVER. + RET C ; Ignore if not one of these. + + JR L0869 ; Jump ahead to handle INK, PAPER, FLASH, BRIGHT, INVERSE, OVER. + +;Handle AT and TAB + +L0860: LD (#5C0E),A ; TV_DATA. Store the control code for use later, #16 (AT) or #17 (TAB). + LD A,#02 ; Two parameters expected (even for TAB). + LD (TVPARS),A ; #5B65. + RET ; Return with zero flag set. + +;Handle INK, PAPER, FLASH, BRIGHT, INVERSE, OVER + +L0869: LD (#5C0E),A ; TV_DATA. Store the control code for use later. + LD A,#02 ; Two parameters expected. [*BUG* - Should be 1 parameter. 'LPRINT INK 4' will produce error report 'C Nonsense in BASIC'. Credit: Toni Baker, ZX Computing Monthly]. + LD (TVPARS),A ; #5B65. + RET ; [*BUG* - Should return with the carry flag reset and the zero flag set. It causes a statement such as 'LPRINT INK 1;' to produce error report '8 End of file'. + ; It is due to the main RS232 processing loop using the state of the flags to determine the success/failure response of the RS232 output routine. Credit: Ian Collier (+3), Andrew Owen (128)] + ; [The bug can be fixed by inserting a XOR A instruction before the RET instruction. Credit: Paul Farrow] + +;All parameters processed + +L0872: LD D,A ; D=Character to print. + LD A,(#5C0E) ; TV_DATA. Fetch the control code. + CP #16 ; Is it AT? + JR Z,L0882 ; Jump ahead to handle AT parameter. + + CP #17 ; Is it TAB? + CCF ; [*BUG* - Should return with the carry flag reset and the zero flag set. It causes a statement such as 'LPRINT INK 1;' to produce error report '8 End of file'. + ; It is due to the main RS232 processing loop using the state of the flags to determine the success/failure response of the RS232 output routine. Credit: Toni Baker, ZX Computing Monthly] + RET NZ ; Ignore if not TAB. + +; [The bug can be fixed by replacing the instructions CCF and RET NZ with the following. Credit: Paul Farrow. +; +; JR Z,NOT_TAB +; +; XOR A +; RET +; +;NOT_TAB: ; ] + +;Handle TAB parameter + + LD A,(#5C0F) ; TV_DATA+1. Fetch the saved parameter. + LD D,A ; Fetch parameter to D. + +;Process AT and TAB + +L0882: LD A,(WIDTH) ; #5B64. + CP D ; Reached end of row? + JR Z,L088A ; Jump ahead if so. + + JR NC,L0890 ; Jump ahead if before end of row. + +;Column position equal or greater than length of row requested + +L088A: LD B,A ; (WIDTH). + LD A,D ; TAB/AT column position. + SUB B ; TAB/AT position - WIDTH. + LD D,A ; The new required column position. + JR L0882 ; Handle the new TAB/AT position. + +L0890: LD A,D ; Fetch the desired column number. + OR A ; + JP Z,L0822 ; Jump to output a carriage return if column 0 required. + +L0895: LD A,(COL) ; #5B63. Fetch the current column position. + CP D ; Compare against desired column position. + RET Z ; Done if reached requested column. + + PUSH DE ; Save the number of spaces to output. + LD A,#20 ; + CALL L07CA ; Output a space via a recursive call. + POP DE ; Retrieve number of spaces to output. + JR L0895 ; Keep outputting spaces until desired column reached. + +; ------------------------ +; Write Byte to RS232 Port +; ------------------------ +;[v]..................................................................................................................... SP2000 [v] + +; The timing of the routine is achieved using the timing constant held in system variable BAUD. +; Entry: A holds character to send. +; Exit: Carry and zero flags reset. + +; L08A3: PUSH AF ; Save the byte to send. + +; LD C,#FD ; +; LD D,#FF ; + +; LD E,#BF ; +; LD B,D ; +; LD A,#0E ; +; OUT (C),A ; Select AY register 14 to control the RS232 port. + +; L08AF: CALL L05D6 ; Check the BREAK key, and produce error message if it is being pressed. + +; IN A,(C) ; Read status of data register. +; AND #40 ; %01000000. Test the DTR line. +; JR NZ,L08AF ; Jump back until device is ready for data. + +; LD HL,(BAUD) ; #5B5F. HL=Baud rate timing constant. +; LD DE,#0002 ; +; OR A ; +; SBC HL,DE ; +; EX DE,HL ; DE=(BAUD)-2. + +; POP AF ; Retrieve the byte to send. +; CPL ; Invert the bits of the byte (RS232 logic is inverted). +; SCF ; Carry is used to send START BIT. +; LD B,#0B ; B=Number of bits to send (1 start + 8 data + 2 stop). + +; DI ; Disable interrupts to ensure accurate timing. + +; ;Transmit each bit + +; L08C8: PUSH BC ; Save the number of bits to send. +; PUSH AF ; Save the data bits. + +; LD A,#FE ; +; LD H,D ; +; LD L,E ; HL=(BAUD)-2. +; LD BC,#BFFD ; AY-3-8912 data register. + +; JP NC,L08DA ; Branch to transmit a 1 or a 0 (initially sending a 0 for the start bit). + +; ;Transmit a 0 + +; AND #F7 ; Clear the RXD (out) line. +; OUT (C),A ; Send out a 0 (high level). +; JR L08E0 ; Jump ahead to continue with next bit. + +; ;Transmit a 1 + +; L08DA: OR #08 ; Set the RXD (out) line. + +L08A3: JR PRINT +L08A5: JR INPUT ; переход на программу ввода через порт принтера + +PRINT: DI + LD E,A + XOR A +; LD BC,1FFDH +; OUT (C),A ; сброс STROBE +.LOOP: CALL L05D6 + XOR A + OUT (Z84.SIO.Ch_B.Ctrl),A + LD A,16 + OUT (Z84.SIO.Ch_B.Ctrl),A + XOR A + OUT (Z84.SIO.Ch_B.Ctrl),A + IN A,(Z84.SIO.Ch_B.Ctrl) + BIT 5,A +// JR Z,LPT_LOOP + JR NZ,.LOOP + LD A,E + OUT (Z84.PIO.Port_A.Data),A + + LD A,5 + OUT (Z84.SIO.Ch_B.Ctrl),A + LD A,#62 ; STROBE !!! + OUT (Z84.SIO.Ch_B.Ctrl),A +L08C8: LD A,5 + OUT (Z84.SIO.Ch_B.Ctrl),A + LD A,#60 + OUT (Z84.SIO.Ch_B.Ctrl),A + +; LD BC,0 +;LPT_PAUSE: +; IN A,(Z84.SIO.Ch_B.Ctrl) +; BIT 5,A +; JR Z,LPT_END +; DEC BC +; LD A,B +; OR C +; JR NZ,LPT_PAUSE +; CALL ERR_RST8 +; DB 20 +LPT_END: +; IN A,(0FEH) +; RLCA +; JR C,LOOP ; ожидание BUSY +; LD D,14 +; CALL 0E7CH ; OUT Sound(D),E +; LD A,20H +; LD BC,1FFDH +; OUT (C),A ; установка STROBE +;LOOP2: DEC A +; JR NZ,LOOP2 ; PAUSE +; OUT (C),A ; сброс STROBE + XOR A + EI + RET + +INPUT: DI +; LD A,20h +; LD BC,1FFDh +; OUT (C),A ; установка BUSY = 0 +LOOP1: CALL L05D6 ; TEST BREAK +; IN A,(0FEh) +; RLCA +; JR C,LOOP1 ; TEST STROBE +; STROBE = 0 + XOR A +; OUT (C),A ; сброс BUSY +; LD A,14 +; CALL 0E89h ; ввод +; PUSH AF +;LOOP3: IN A,(0FEh) +; RLCA +; JR NC,LOOP3 ; TEST STROBE ожидание +; POP AF + SCF ; установка C флага если A не 0 + EI + RET +; +; Конец программы принтера + + +;!FIXIT кусок ниже теперь не используется занять чем-нибудь? [v] +L08DA: EX AF,AF' + + OUT (C),A ; Send out a 1 (low level). + JR L08E0 ; Jump ahead to continue with next bit. + +;Delay the length of a bit +L08E0: DEC HL ; (6) Delay 26*BAUD cycles. + LD A,H ; (4) + OR L ; (4) + JR NZ,L08E0 ; (12) Jump back until delay is completed. + + NOP ; (4) Fine tune the timing. + NOP ; (4) + NOP ; (4) + + POP AF ; Retrieve the data bits to send. + POP BC ; Retrieve the number of bits left to send. + OR A ; Clear carry flag. + RRA ; Shift the next bit to send into the carry flag. + DJNZ L08C8 ; Jump back to send next bit until all bits sent. + + EI ; Re-enable interrupts. + RET ; Return with carry and zero flags reset. +;- - - - - - - - - - - - - - - - - - - - - - - - [^] + +;[^]..................................................................................................................... SP2000 [^] +; + + +; +; -------------------- +; COPY Command Routine +; -------------------- +; This routine copies 22 rows of the screen, outputting them to the printer a +; half row at a time. It is designed for EPSON compatible printers supporting +; double density bit graphics and 7/72 inch line spacing. +; Only the pixel information is processed; the attributes are ignored. + +L08F0: LD HL,HD_0B ; Half row counter. + LD (HL),#2B ; Set the half row counter to 43 half rows (will output 44 half rows in total). + +L08F5: LD HL,L0979 ; Point to printer configuration data (7/72 inch line spacing, double density bit graphics). + CALL L095F ; Send the configuration data to printer. + + CALL L0915 ; Output a half row, at double height. + + LD HL,L0980 ; Table holds a line feed only. + CALL L095F ; Send a line feed to printer. + + LD HL,HD_0B ; #5B72. The half row counter is tested to see if it is zero + XOR A ; and if so then the line spacing is reset to its + CP (HL) ; original value. + JR Z,L090E ; Jump if done, resetting printer line spacing. + + DEC (HL) ; Decrement half row counter. + JR L08F5 ; Repeat for the next half row. + +;Copy done so reset printer line spacing before exiting + +L090E: LD HL,L0982 ; Point to printer configuration data (1/6 inch line spacing). + CALL L095F ; Send the configuration data to printer. + RET ; [Could have saved 1 byte by using JP #095F (ROM 0)] + +; --------------- +; Output Half Row +; --------------- + +L0915: LD HL,HD_00 ; #5B71. Pixel column counter. + LD (HL),#FF ; Set pixel column counter to 255 pixels. + +L091A: CALL L0926 ; Output a column of pixels, at double height. + + LD HL,HD_00 ; #5B71. Pixel column counter. + XOR A ; + CP (HL) ; Check if all pixels in this row have been output. + RET Z ; Return if so. + + DEC (HL) ; Decrement pixel column counter. + JR L091A ; Repeat for all pixels in this row. + +;Output a column of pixels (at double height) + +L0926: LD DE,#C000 ; D=%11000000. Used to hold the double height pixel. + LD BC,(HD_00) ; #5B71. C=Pixel column counter, B=Half row counter. + SCF ; + RL B ; B=2xB+1 + SCF ; + RL B ; B=4xB+3. The pixel row coordinate. + + LD A,C ; Pixel column counter. + CPL ; + LD C,A ; C=255-C. The pixel column coordinate. + + XOR A ; Clear A. Used to generate double height nibble of pixels to output. + PUSH AF ; + + PUSH DE ; + PUSH BC ; Save registers. + +L093A: CALL L096D ; Test whether pixel (B,C) is set + + POP BC ; + POP DE ; Restore registers. + + LD E,#00 ; Set double height pixel = 0. + JR Z,L0944 ; Jump if pixel is reset. + + LD E,D ; The double height pixel to output (%11000000, %00110000, %00001100 or %00000011). + +L0944: POP AF ; + OR E ; Add the double height pixel value to the byte to output. + PUSH AF ; + + DEC B ; Decrement half row coordinate. + SRL D ; + SRL D ; Create next double height pixel value (%00110000, %00001100 or %00000011). + + PUSH DE ; + PUSH BC ; + JR NC,L093A ; Repeat for all four pixels in the half row. + + POP BC ; + POP DE ; Unload the stack. + + POP AF ; + LD B,#03 ; Send double height nibble of pixels output 3 times. + +; ----------------------- +; Output Nibble of Pixels +; ----------------------- +; Send each nibble of pixels (i.e. column of 4 pixels) output 3 times so that +; the width of a pixel is the same size as its height. + +L0955: PUSH BC ; + PUSH AF ; + CALL L08A3 ; Send byte to RS232 port. + POP AF ; + POP BC ; + DJNZ L0955 ; + + RET ; + +; ---------------------------- +; Output Characters from Table +; ---------------------------- +; This routine is used to send a sequence of EPSON printer control codes out to the RS232 port. +; It sends (HL) characters starting from HL+1. + +L095F: LD B,(HL) ; Get number of bytes to send. + INC HL ; Point to the data to send. + +L0961: LD A,(HL) ; Retrieve value. + PUSH HL ; + PUSH BC ; + CALL L08A3 ; Send byte to RS232 port. + POP BC ; + POP HL ; + INC HL ; Point to next data byte to send. + DJNZ L0961 ; Repeat for all bytes. + + RET ; + +; ------------------------------- +; Test Whether Pixel (B,C) is Set +; ------------------------------- +; Entry: B=Pixel line +; C=Pixel column. +; Exit : A=#00 if pixel is reset +; A>#00 if pixel is set (actually the value of the bit corresponding to the pixel within the byte). + +L096D: RST #28 ; Get address of (B,C) pixel into HL and pixel position within byte into A. + DEFW PIXEL_ADDR ; #22AA. + LD B,A ; B=Pixel position within byte (0-7). + INC B ; + + XOR A ; Pixel mask. + SCF ; Carry flag holds bit to be rotated into the mask. + +L0974: RRA ; Shift the mask bit into the required bit position. + DJNZ L0974 ; + + AND (HL) ; Isolate this pixel from A. + RET ; + +; --------------------------------- +; EPSON Printer Control Code Tables +; --------------------------------- + +L0979: DEFB #06 ; 6 characters follow. + DEFB #1B, #31 ; ESC '1' - 7/72 inch line spacing. + DEFB #1B, #4C, #00, #03 ; ESC 'L' 0 3 - Double density (768 bytes per row). + +L0980: DEFB #01 ; 1 character follows. + DEFB #0A ; Line feed. + +L0982: DEFB #02 ; 2 characters follow. + DEFB #1B, #32 ; ESC '2' - 1/6 inch line spacing. + + +; ===================== +; PLAY COMMAND ROUTINES +; ===================== +; Up to 3 channels of music/noise are supported by the AY-3-8912 sound generator. +; Up to 8 channels of music can be sent to support synthesisers, drum machines or sequencers via the MIDI interface, +; with the first 3 channels also played by the AY-3-8912 sound generator. For each channel of music, a MIDI channel +; can be assigned to it using the 'Y' command. +; +; The PLAY command reserves and initialises space for the PLAY command. This comprises a block of #003C bytes +; used to manage the PLAY command (IY points to this command data block) and a block of #0037 bytes for each +; channel string (IX is used to point to the channel data block for the current channel). [Note that the command +; data block is #04 bytes larger than it needs to be, and each channel data block is #11 bytes larger than it +; needs to be] +; +; Entry: B=The number of strings in the PLAY command (1..8). + +; ------------------------- +; Command Data Block Format +; ------------------------- +; IY+#00 / IY+#01 = Channel 0 data block pointer. Points to the data for channel 0 (string 1). +; IY+#02 / IY+#03 = Channel 1 data block pointer. Points to the data for channel 1 (string 2). +; IY+#04 / IY+#05 = Channel 2 data block pointer. Points to the data for channel 2 (string 3). +; IY+#06 / IY+#07 = Channel 3 data block pointer. Points to the data for channel 3 (string 4). +; IY+#08 / IY+#09 = Channel 4 data block pointer. Points to the data for channel 4 (string 5). +; IY+#0A / IY+#0B = Channel 5 data block pointer. Points to the data for channel 5 (string 6). +; IY+#0C / IY+#0D = Channel 6 data block pointer. Points to the data for channel 6 (string 7). +; IY+#0E / IY+#0F = Channel 7 data block pointer. Points to the data for channel 7 (string 8). +; IY+#10 = Channel bitmap. Initialised to #FF and a 0 rotated in to the left for each string parameters +; of the PLAY command, thereby indicating the channels in use. +; IY+#11 / IY+#12 = Channel data block duration pointer. Points to duration length store in channel 0 data block (string 1). +; IY+#13 / IY+#14 = Channel data block duration pointer. Points to duration length store in channel 1 data block (string 2). +; IY+#15 / IY+#16 = Channel data block duration pointer. Points to duration length store in channel 2 data block (string 3). +; IY+#17 / IY+#18 = Channel data block duration pointer. Points to duration length store in channel 3 data block (string 4). +; IY+#19 / IY+#1A = Channel data block duration pointer. Points to duration length store in channel 4 data block (string 5). +; IY+#1B / IY+#1C = Channel data block duration pointer. Points to duration length store in channel 5 data block (string 6). +; IY+#1D / IY+#1E = Channel data block duration pointer. Points to duration length store in channel 6 data block (string 7). +; IY+#1F / IY+#20 = Channel data block duration pointer. Points to duration length store in channel 7 data block (string 8). +; IY+#21 = Channel selector. It is used as a shift register with bit 0 initially set and then shift to the left +; until a carry occurs, thereby indicating all 8 possible channels have been processed. +; IY+#22 = Temporary channel bitmap, used to hold a working copy of the channel bitmap at IY+#10. +; IY+#23 / IY+#24 = Address of the channel data block pointers, or address of the channel data block duration pointers +; (allows the routine at #0A6E (ROM 0) to be used with both set of pointers). +; IY+#25 / IY+#26 = Stores the smallest duration length of all currently playing channel notes. +; IY+#27 / IY+#28 = The current tempo timing value (derived from the tempo parameter 60..240 beats per second). +; IY+#29 = The current effect waveform value. +; IY+#2A = Temporary string counter selector. +; IY+#2B..IY+#37 = Holds a floating point calculator routine. +; IY+#38..IY+#3B = Not used. + +; ------------------------- +; Channel Data Block Format +; ------------------------- +; IX+#00 = The note number being played on this channel (equivalent to index offset into the note table). +; IX+#01 = MIDI channel assigned to this string (range 0 to 15). +; IX+#02 = Channel number (range 0 to 7), i.e. index position of the string within the PLAY command. +; IX+#03 = 12*Octave number (0, 12, 24, 36, 48, 60, 72, 84 or 96). +; IX+#04 = Current volume (range 0 to 15, or if bit 4 set then using envelope). +; IX+#05 = Last note duration value as specified in the string (range 1 to 9). +; IX+#06 / IX+#07 = Address of current position in the string. +; IX+#08 / IX+#09 = Address of byte after the end of the string. +; IX+#0A = Flags: +; Bit 0 : 1=Single closing bracket found (repeat string indefinitely). +; Bits 1-7: Not used (always 0). +; IX+#0B = Open bracket nesting level (range #00 to #04). +; IX+#0C / IX+#0D = Return address for opening bracket nesting level 0 (points to character after the bracket). +; IX+#0E / IX+#0F = Return address for opening bracket nesting level 1 (points to character after the bracket). +; IX+#10 / IX+#11 = Return address for opening bracket nesting level 2 (points to character after the bracket). +; IX+#12 / IX+#13 = Return address for opening bracket nesting level 3 (points to character after the bracket). +; IX+#14 / IX+#15 = Return address for opening bracket nesting level 4 (points to character after the bracket). +; IX+#16 = Closing bracket nesting level (range #FF to #04). +; IX+#17...IX+#18 = Return address for closing bracket nesting level 0 (points to character after the bracket). +; IX+#19...IX+#1A = Return address for closing bracket nesting level 1 (points to character after the bracket). +; IX+#1B...IX+#1C = Return address for closing bracket nesting level 2 (points to character after the bracket). +; IX+#1D...IX+#1E = Return address for closing bracket nesting level 3 (points to character after the bracket). +; IX+#1F...IX+#20 = Return address for closing bracket nesting level 4 (points to character after the bracket). +; IX+#21 = Tied notes counter (for a single note the value is 1). +; IX+#22 / IX+#23 = Duration length, specified in 96ths of a note. +; IX+#24...IX+#25 = Subsequent note duration length (used only with triplets), specified in 96ths of a note. +; IX+#26...IX+#36 = Not used. + +L0985: DI ; Disable interrupts to ensure accurate timing. + +;Create a workspace for the play channel command strings + + PUSH BC ; B=Number of channel string (range 1 to 8). Also used as string index number in the following loop. + + LD DE,#0037 ; + LD HL,#003C ; + +L098D: ADD HL,DE ; Calculate HL=#003C + (#0037 * B). + DJNZ L098D ; + + LD C,L ; + LD B,H ; BC=Space required (maximum = #01F4). + RST #28 ; + DEFW BC_SPACES ; #0030. Make BC bytes of space in the workspace. + + DI ; Interrupts get re-enabled by the call mechanism to ROM 1 so disable them again. + + PUSH DE ; + POP IY ; IY=Points at first new byte - the command data block. + + PUSH HL ; + POP IX ; IX=Points at last new byte - byte after all channel information blocks. + + LD (IY+#10),#FF ; Initial channel bitmap with value meaning 'zero strings' + +;Loop over each string to be played + +L09A0: LD BC,#FFC9 ; #-37 (#37 bytes is the size of a play channel string information block). + ADD IX,BC ; IX points to start of space for the last channel. + LD (IX+#03),#3C ; Default octave is 5. + LD (IX+#01),#FF ; No MIDI channel assigned. + LD (IX+#04),#0F ; Default volume is 15. + LD (IX+#05),#05 ; Default note duration. + LD (IX+#21),#00 ; Count of the number of tied notes. + LD (IX+#0A),#00 ; Signal not to repeat the string indefinitely. + LD (IX+#0B),#00 ; No opening bracket nesting level. + LD (IX+#16),#FF ; No closing bracket nesting level. + LD (IX+#17),#00 ; Return address for closing bracket nesting level 0. + LD (IX+#18),#00 ; [No need to initialise this since it is written to before it is ever tested] + +; [*BUG* - At this point interrupts are disabled and IY is now being used as a pointer to the master +; PLAY information block. Unfortunately, interrupts are enabled during the STK_FETCH call and +; IY is left containing the wrong value. This means that if an interrupt were to occur during +; execution of the subroutine then there would be a one in 65536 chance that (IY+#40) will be +; corrupted - this corresponds to the volume setting for music channel A. +; Rewriting the SWAP routine to only re-enable interrupts if they were originally enabled +; would cure this bug (see end of file for description of her suggested fix). Credit: Toni Baker, ZX Computing Monthly] + +; [An alternative and simpler solution to the fix Toni Baker describes would be to stack IY, set IY to point +; to the system variables at #5C3A, call STK_FETCH, disable interrupts, then pop the stacked value back to IY. Credit: Paul Farrow] + + RST #28 ; Get the details of the string from the stack. + DEFW STK_FETCH ; #2BF1. + + DI ; Interrupts get re-enabled by the call mechanism to ROM 1 so disable them again. + + LD (IX+#06),E ; Store the current position within in the string, i.e. the beginning of it. + LD (IX+#07),D ; + LD (IX+#0C),E ; Store the return position within the string for a closing bracket, + LD (IX+#0D),D ; which is initially the start of the string in case a single closing bracket is found. + + EX DE,HL ; HL=Points to start of string. BC=Length of string. + ADD HL,BC ; HL=Points to address of byte after the string. + LD (IX+#08),L ; Store the address of the character just + LD (IX+#09),H ; after the string. + + POP BC ; B=String index number (range 1 to 8). + PUSH BC ; Save it on the stack again. + DEC B ; Reduce the index so it ranges from 0 to 7. + + LD C,B ; + LD B,#00 ; + SLA C ; BC=String index*2. + + PUSH IY ; + POP HL ; HL=Address of the command data block. + ADD HL,BC ; Skip 8 channel data pointer words. + + PUSH IX ; + POP BC ; BC=Address of current channel information block. + + LD (HL),C ; Store the pointer to the channel information block. + INC HL ; + LD (HL),B ; + + OR A ; Clear the carry flag. + RL (IY+#10) ; Rotate one zero-bit into the least significant bit of the channel bitmap. + ; This initially holds #FF but once this loop is over, this byte has + ; a zero bit for each string parameter of the PLAY command. + + POP BC ; B=Current string index. + DEC B ; Decrement string index so it ranges from 0 to 7. + PUSH BC ; Save it for future use on the next iteration. + LD (IX+#02),B ; Store the channel number. + + JR NZ,L09A0 ; Jump back while more channel strings to process. + + POP BC ; Drop item left on the stack. + +;Entry point here from the vector table at #011B + +L0A05: LD (IY+#27),#1A ; Set the initial tempo timing value. + LD (IY+#28),#0B ; Corresponds to a 'T' command value of 120, and gives two crotchets per second. + + PUSH IY ; + POP HL ; HL=Points to the command data block. + + LD BC,#002B ; + ADD HL,BC ; + EX DE,HL ; DE=Address to store RAM routine. + LD HL,L0A31 ; HL=Address of the RAM routine bytes. + LD BC,#000D ; + LDIR ; Copy the calculator routine to RAM. + + LD D,#07 ; Register 7 - Mixer. + LD E,#F8 ; I/O ports are inputs, noise output off, tone output on. + CALL L0E7C ; Write to sound generator register. + + LD D,#0B ; Register 11 - Envelope Period (Fine). + LD E,#FF ; Set period to maximum. + CALL L0E7C ; Write to sound generator register. + + INC D ; Register 12 - Envelope Period (Coarse). + CALL L0E7C ; Write to sound generator register. + + JR L0A7D ; Jump ahead to continue. + ; [Could have saved these 2 bytes by having the code at #0A7D (ROM 0) immediately follow] + +; ------------------------------------------------- +; Calculate Timing Loop Counter <<< RAM Routine >>> +; ------------------------------------------------- +; This routine is copied into the command data block (offset #2B..#37) by +; the routine at #0A05 (ROM 0). +; It uses the floating point calculator found in ROM 1, which is usually +; invoked via a RST #28 instruction. Since ROM 0 uses RST #28 to call a +; routine in ROM 1, it is unable to invoke the floating point calculator +; this way. It therefore copies the following routine to RAM and calls it +; with ROM 1 paged in. +; +; The routine calculates (10/x)/7.33e-6, where x is the tempo 'T' parameter value +; multiplied by 4. The result is used an inner loop counter in the wait routine at #0F76 (ROM 0). +; Each iteration of this loop takes 26 T-states. The time taken by 26 T-states +; is 7.33e-6 seconds. So the total time for the loop to execute is 2.5/TEMPO seconds. +; +; Entry: The value 4*TEMPO exists on the calculator stack (where TEMPO is in the range 60..240). +; Exit : The calculator stack holds the result. + +L0A31: RST 28H ; Invoke the floating point calculator. + DEFB #A4 ; stk-ten. = x, 10 + DEFB #01 ; exchange. = 10, x + DEFB #05 ; division. = 10/x + DEFB #34 ; stk-data. = 10/x, 7.33e-6 + DEFB #DF ; - exponent #6F (floating point number 7.33e-6). + DEFB #75 ; - mantissa byte 1 + DEFB #F4 ; - mantissa byte 2 + DEFB #38 ; - mantissa byte 3 + DEFB #75 ; - mantissa byte 4 + DEFB #05 ; division. = (10/x)/7.33e-6 + DEFB #38 ; end-calc. + RET ; + +; -------------- +; Test BREAK Key +; -------------- +; Test for BREAK being pressed. +; Exit: Carry flag reset if BREAK is being pressed. + +L0A3E: LD A,#7F ; + IN A,(#FE) ; + RRA ; + RET C ; Return with carry flag set if SPACE not pressed. + + LD A,#FE ; + IN A,(#FE) ; + RRA ; + RET ; Return with carry flag set if CAPS not pressed. + +; ------------------------------------------- +; Select Channel Data Block Duration Pointers +; ------------------------------------------- +; Point to the start of the channel data block duration pointers within the command data block. +; Entry: IY=Address of the command data block. +; Exit : HL=Address of current channel pointer. + +L0A4A: LD BC,#0011 ; Offset to the channel data block duration pointers table. + JR L0A52 ; Jump ahead to continue. + +; ---------------------------------- +; Select Channel Data Block Pointers +; ---------------------------------- +; Point to the start of the channel data block pointers within the command data block. +; Entry: IY=Address of the command data block. +; Exit : HL=Address of current channel pointer. + +L0A4F: LD BC,#0000 ; Offset to the channel data block pointers table. + +L0A52: PUSH IY ; + POP HL ; HL=Point to the command data block. + + ADD HL,BC ; Point to the desired channel pointers table. + + LD (IY+#23),L ; + LD (IY+#24),H ; Store the start address of channels pointer table. + + LD A,(IY+#10) ; Fetch the channel bitmap. + LD (IY+#22),A ; Initialise the working copy. + + LD (IY+#21),#01 ; Channel selector. Set the shift register to indicate the first channel. + RET ; + +; ------------------------------------------------- +; Get Channel Data Block Address for Current String +; ------------------------------------------------- +; Entry: HL=Address of channel data block pointer. +; Exit : IX=Address of current channel data block. + +L0A67: LD E,(HL) ; + INC HL ; + LD D,(HL) ; Fetch the address of the current channel data block. + + PUSH DE ; + POP IX ; Return it in IX. + RET ; + +; ------------------------- +; Next Channel Data Pointer +; ------------------------- + +L0A6E: LD L,(IY+#23) ; The address of current channel data pointer. + LD H,(IY+#24) ; + INC HL ; + INC HL ; Advance to the next channel data pointer. + LD (IY+#23),L ; + LD (IY+#24),H ; The address of new channel data pointer. + RET ; + +; --------------------------- +; PLAY Command (Continuation) +; --------------------------- +; This section is responsible for processing the PLAY command and is a continuation of the routine +; at #0985 (ROM 0). It begins by determining the first note to play on each channel and then enters +; a loop to play these notes, fetching the subsequent notes to play at the appropriate times. + +L0A7D: CALL L0A4F ; Select channel data block pointers. + +L0A80: RR (IY+#22) ; Working copy of channel bitmap. Test if next string present. + JR C,L0A8C ; Jump ahead if there is no string for this channel. + +;HL=Address of channel data pointer. + + CALL L0A67 ; Get address of channel data block for the current string into IX. + CALL L0B5C ; Find the first note to play for this channel from its play string. + +L0A8C: SLA (IY+#21) ; Have all channels been processed? + JR C,L0A97 ; Jump ahead if so. + + CALL L0A6E ; Advance to the next channel data block pointer. + JR L0A80 ; Jump back to process the next channel. + +;The first notes to play for each channel have now been determined. A loop is entered that coordinates playing +;the notes and fetching subsequent notes when required. Notes across channels may be of different lengths and +;so the shortest one is determined, the tones for all channels set and then a waiting delay entered for the shortest +;note delay. This delay length is then subtracted from all channel note lengths to leave the remaining lengths that +;each note needs to be played for. For the channel with the smallest note length, this will now have completely played +;and so a new note is fetched for it. The smallest length of the current notes is then determined again and the process +;described above repeated. A test is made on each iteration to see if all channels have run out of data to play, and if +;so this ends the PLAY command. + +L0A97: CALL L0F91 ; Find smallest duration length of the current notes across all channels. + + PUSH DE ; Save the smallest duration length. + CALL L0F42 ; Play a note on each channel. + POP DE ; DE=The smallest duration length. + +L0A9F: LD A,(IY+#10) ; Channel bitmap. + CP #FF ; Is there anything to play? + JR NZ,L0AAB ; Jump if there is. + + CALL L0E93 ; Turn off all sound and restore IY. + EI ; Re-enable interrupts. + RET ; End of play command. + +L0AAB: DEC DE ; DE=Smallest channel duration length, i.e. duration until the next channel state change. + CALL L0F76 ; Perform a wait. + CALL L0FC1 ; Play a note on each channel and update the channel duration lengths. + + CALL L0F91 ; Find smallest duration length of the current notes across all channels. + JR L0A9F ; Jump back to see if there is more to process. + +; ---------------------------- +; PLAY Command Character Table +; ---------------------------- +; Recognised characters in PLAY commands. + +L0AB7: DEFM "HZYXWUVMT)(NO!" + +; ------------------ +; Get Play Character +; ------------------ +; Get the current character from the PLAY string and then increment the +; character pointer within the string. +; Exit: Carry flag set if string has been fully processed. +; Carry flag reset if character is available. +; A=Character available. + +L0AC5: CALL L0EE3 ; Get the current character from the play string for this channel. + RET C ; Return if no more characters. + + INC (IX+#06) ; Increment the low byte of the string pointer. + RET NZ ; Return if it has not overflowed. + + INC (IX+#07) ; Else increment the high byte of the string pointer. + RET ; Returns with carry flag reset. + +; -------------------------- +; Get Next Note in Semitones +; -------------------------- +; Finds the number of semitones above C for the next note in the string, +; Entry: IX=Address of the channel data block. +; Exit : A=Number of semitones above C, or #80 for a rest. + +L0AD1: PUSH HL ; Save HL. + + LD C,#00 ; Default is for a 'natural' note, i.e. no adjustment. + +L0AD4: CALL L0AC5 ; Get the current character from the PLAY string, and advance the position pointer. + JR C,L0AE1 ; Jump if at the end of the string. + + CP '&' ; #26. Is it a rest? + JR NZ,L0AEC ; Jump ahead if not. + + LD A,#80 ; Signal that it is a rest. + +L0ADF: POP HL ; Restore HL. + RET ; + +L0AE1: LD A,(IY+#21) ; Fetch the channel selector. + OR (IY+#10) ; Clear the channel flag for this string. + LD (IY+#10),A ; Store the new channel bitmap. + JR L0ADF ; Jump back to return. + +L0AEC: CP '#' ; #23. Is it a sharpen? + JR NZ,L0AF3 ; Jump ahead if not. + + INC C ; Increment by a semitone. + JR L0AD4 ; Jump back to get the next character. + +L0AF3: CP '$' ; #24. Is it a flatten? + JR NZ,L0AFA ; Jump ahead if not. + + DEC C ; Decrement by a semitone. + JR L0AD4 ; Jump back to get the next character. + +L0AFA: BIT 5,A ; Is it a lower case letter? + JR NZ,L0B04 ; Jump ahead if lower case. + + PUSH AF ; It is an upper case letter so + LD A,#0C ; increase an octave + ADD A,C ; by adding 12 semitones. + LD C,A ; + POP AF ; + +L0B04: AND #DF ; Convert to upper case. + SUB #41 ; Reduce to range 'A'->0 .. 'G'->6. + JP C,L0F22 ; Jump if below 'A' to produce error report "k Invalid note name". + + CP #07 ; Is it 7 or above? + JP NC,L0F22 ; Jump if so to produce error report "k Invalid note name". + + PUSH BC ; C=Number of semitones. + + LD B,#00 ; + LD C,A ; BC holds 0..6 for 'a'..'g'. + LD HL,L0DF9 ; Look up the number of semitones above note C for the note. + ADD HL,BC ; + LD A,(HL) ; A=Number of semitones above note C. + + POP BC ; C=Number of semitones due to sharpen/flatten characters. + ADD A,C ; Adjust number of semitones above note C for the sharpen/flatten characters. + + POP HL ; Restore HL. + RET ; + +; ---------------------------------- +; Get Numeric Value from Play String +; ---------------------------------- +; Get a numeric value from a PLAY string, returning 0 if no numeric value present. +; Entry: IX=Address of the channel data block. +; Exit : BC=Numeric value, or 0 if no numeric value found. + +L0B1D: PUSH HL ; Save registers. + PUSH DE ; + + LD L,(IX+#06) ; Get the pointer into the PLAY string. + LD H,(IX+#07) ; + + LD DE,#0000 ; Initialise result to 0. + +L0B28: LD A,(HL) ; + CP '0' ; #30. Is character numeric? + JR C,L0B45 ; Jump ahead if not. + + CP ':' ; #3A. Is character numeric? + JR NC,L0B45 ; Jump ahead if not. + + INC HL ; Advance to the next character. + PUSH HL ; Save the pointer into the string. + + CALL L0B50 ; Multiply result so far by 10. + SUB '0' ; #30. Convert ASCII digit to numeric value. + LD H,#00 ; + LD L,A ; HL=Numeric digit value. + ADD HL,DE ; Add the numeric value to the result so far. + JR C,L0B42 ; Jump ahead if an overflow to produce error report "l number too big". + + EX DE,HL ; Transfer the result into DE. + + POP HL ; Retrieve the pointer into the string. + JR L0B28 ; Loop back to handle any further numeric digits. + +L0B42: JP L0F1A ; Jump to produce error report "l number too big". + ; [Could have saved 1 byte by directly using JP C,#0F1A (ROM 0) instead of using this JP and + ; the two JR C,#0B42 (ROM 0) instructions that come here] + +;The end of the numeric value was reached + +L0B45: LD (IX+#06),L ; Store the new pointer position into the string. + LD (IX+#07),H ; + + PUSH DE ; + POP BC ; Return the result in BC. + + POP DE ; Restore registers. + POP HL ; + RET ; + +; ----------------- +; Multiply DE by 10 +; ----------------- +; Entry: DE=Value to multiple by 10. +; Exit : DE=Value*10. + +L0B50: LD HL,#0000 ; + LD B,#0A ; Add DE to HL ten times. + +L0B55: ADD HL,DE ; + JR C,L0B42 ; Jump ahead if an overflow to produce error report "l number too big". + + DJNZ L0B55 ; + + EX DE,HL ; Transfer the result into DE. + RET ; + +; ---------------------------------- +; Find Next Note from Channel String +; ---------------------------------- +; Entry: IX=Address of channel data block. + +L0B5C: CALL L0A3E ; Test for BREAK being pressed. + JR C,L0B69 ; Jump ahead if not pressed. + + CALL L0E93 ; Turn off all sound and restore IY. + EI ; Re-enable interrupts. + + CALL L05AC ; Produce error report. [Could have saved 1 byte by using JP #05D6 (ROM 0)] + DEFB #14 ; "L Break into program" + +L0B69: CALL L0AC5 ; Get the current character from the PLAY string, and advance the position pointer. + JP C,L0DA2 ; Jump if at the end of the string. + + CALL L0DF0 ; Find the handler routine for the PLAY command character. + + LD B,#00 ; + SLA C ; Generate the offset into the + LD HL,L0DCA ; command vector table. + ADD HL,BC ; HL points to handler routine for this command character. + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; Fetch the handler routine address. + + EX DE,HL ; HL=Handler routine address for this command character. + CALL L0B84 ; Make an indirect call to the handler routine. + JR L0B5C ; Jump back to handle the next character in the string. + +;Comes here after processing a non-numeric digit that does not have a specific command routine handler +;Hence the next note to play has been determined and so a return is made to process the other channels. + +L0B83: RET ; Just make a return. + +L0B84: JP (HL) ; Jump to the command handler routine. + +; -------------------------- +; Play Command '!' (Comment) +; -------------------------- +; A comment is enclosed within exclamation marks, e.g. "! A comment !". +; Entry: IX=Address of the channel data block. + +L0B85: CALL L0AC5 ; Get the current character from the PLAY string, and advance the position pointer. + JP C,L0DA1 ; Jump if at the end of the string. + + CP '!' ; #21. Is it the end-of-comment character? + RET Z ; Return if it is. + + JR L0B85 ; Jump back to test the next character. + +; ------------------------- +; Play Command 'O' (Octave) +; ------------------------- +; The 'O' command is followed by a numeric value within the range 0 to 8, +; although due to loose range checking the value MOD 256 only needs to be +; within 0 to 8. Hence O256 operates the same as O0. +; Entry: IX=Address of the channel data block. + +L0B90: CALL L0B1D ; Get following numeric value from the string into BC. + + LD A,C ; Is it between 0 and 8? + CP #09 ; + JP NC,L0F12 ; Jump if above 8 to produce error report "n Out of range". + + SLA A ; Multiply A by 12. + SLA A ; + LD B,A ; + SLA A ; + ADD A,B ; + + LD (IX+#03),A ; Store the octave value. + RET ; + +; ---------------------------- +; Play Command 'N' (Separator) +; ---------------------------- +; The 'N' command is simply a separator marker and so is ignored. +; Entry: IX=Address of the channel data block. + +L0BA5: RET ; Nothing to do so make an immediate return. + +; ---------------------------------- +; Play Command '(' (Start of Repeat) +; ---------------------------------- +; A phrase can be enclosed within brackets causing it to be repeated, i.e. played twice. +; Entry: IX=Address of the channel data block. + +L0BA6: LD A,(IX+#0B) ; A=Current level of open bracket nesting. + INC A ; Increment the count. + CP #05 ; Only 4 levels supported. + JP Z,L0F2A ; Jump if this is the fifth to produce error report "d Too many brackets". + + LD (IX+#0B),A ; Store the new open bracket nesting level. + + LD DE,#000C ; Offset to the bracket level return position stores. + CALL L0C27 ; HL=Address of the pointer in which to store the return location of the bracket. + + LD A,(IX+#06) ; Store the current string position as the return address of the open bracket. + LD (HL),A ; + INC HL ; + LD A,(IX+#07) ; + LD (HL),A ; + RET ; + +; -------------------------------- +; Play Command ')' (End of Repeat) +; -------------------------------- +; A phrase can be enclosed within brackets causing it to be repeated, i.e. played twice. +; Brackets can also be nested within each other, to 4 levels deep. +; If a closing bracket if used without a matching opening bracket then the whole string up +; until that point is repeated indefinitely. +; Entry: IX=Address of the channel data block. + +L0BC2: LD A,(IX+#16) ; Fetch the nesting level of closing brackets. + LD DE,#0017 ; Offset to the closing bracket return address store. + OR A ; Is there any bracket nesting so far? + JP M,L0BF0 ; Jump if none. [Could have been faster by jumping to #0BF3 (ROM 0)] + +;Has the bracket level been repeated, i.e. re-reached the same position in the string as the closing bracket return address? + + CALL L0C27 ; HL=Address of the pointer to the corresponding closing bracket return address store. + LD A,(IX+#06) ; Fetch the low byte of the current address. + CP (HL) ; Re-reached the closing bracket? + JR NZ,L0BF0 ; Jump ahead if not. + + INC HL ; Point to the high byte. + LD A,(IX+#07) ; Fetch the high byte address of the current address. + CP (HL) ; Re-reached the closing bracket? + JR NZ,L0BF0 ; Jump ahead if not. + +;The bracket level has been repeated. Now check whether this was the outer bracket level. + + DEC (IX+#16) ; Decrement the closing bracket nesting level since this level has been repeated. + LD A,(IX+#16) ; [There is no need for the LD A,(IX+#16) and OR A instructions since the DEC (IX+#16) already set the flags] + OR A ; Reached the outer bracket nesting level? + RET P ; Return if not the outer bracket nesting level such that the character + ; after the closing bracket is processed next. + +;The outer bracket level has been repeated + + BIT 0,(IX+#0A) ; Was this a single closing bracket? + RET Z ; Return if it was not. + +;The repeat was caused by a single closing bracket so re-initialise the repeat + + LD (IX+#16),#00 ; Restore one level of closing bracket nesting. + XOR A ; Select closing bracket nesting level 0. + JR L0C0B ; Jump ahead to continue. + +;A new level of closing bracket nesting + +L0BF0: LD A,(IX+#16) ; Fetch the nesting level of closing brackets. + INC A ; Increment the count. + CP #05 ; Only 5 levels supported (4 to match up with opening brackets and a 5th to repeat indefinitely). + JP Z,L0F2A ; Jump if this is the fifth to produce error report "d Too many brackets". + + LD (IX+#16),A ; Store the new closing bracket nesting level. + + CALL L0C27 ; HL=Address of the pointer to the appropriate closing bracket return address store. + + LD A,(IX+#06) ; Store the current string position as the return address for the closing bracket. + LD (HL),A ; + INC HL ; + LD A,(IX+#07) ; + LD (HL),A ; + + LD A,(IX+#0B) ; Fetch the nesting level of opening brackets. + +L0C0B: LD DE,#000C ; + CALL L0C27 ; HL=Address of the pointer to the opening bracket nesting level return address store. + + LD A,(HL) ; Set the return address of the nesting level's opening bracket + LD (IX+#06),A ; as new current position within the string. + INC HL ; + LD A,(HL) ; For a single closing bracket only, this will be the start address of the string. + LD (IX+#07),A ; + + DEC (IX+#0B) ; Decrement level of open bracket nesting. + RET P ; Return if the closing bracket matched an open bracket. + +;There is one more closing bracket then opening brackets, i.e. repeat string indefinitely + + LD (IX+#0B),#00 ; Set the opening brackets nesting level to 0. + SET 0,(IX+#0A) ; Signal a single closing bracket only, i.e. to repeat the string indefinitely. + RET ; + +; ------------------------------------ +; Get Address of Bracket Pointer Store +; ------------------------------------ +; Entry: IX=Address of the channel data block. +; DE=Offset to the bracket pointer stores. +; A=Index into the bracket pointer stores. +; Exit : HL=Address of the specified pointer store. + +L0C27: PUSH IX ; + POP HL ; HL=IX. + + ADD HL,DE ; HL=IX+DE. + LD B,#00 ; + LD C,A ; + SLA C ; + ADD HL,BC ; HL=IX+DE+2*A. + RET ; + +; ------------------------ +; Play Command 'T' (Tempo) +; ------------------------ +; A temp command must be specified in the first play string and is followed by a numeric +; value in the range 60 to 240 representing the number of beats (crotchets) per minute. +; Entry: IX=Address of the channel data block. + +L0C32: CALL L0B1D ; Get following numeric value from the string into BC. + LD A,B ; + OR A ; + JP NZ,L0F12 ; Jump if 256 or above to produce error report "n Out of range". + + LD A,C ; + CP #3C ; + JP C,L0F12 ; Jump if 59 or below to produce error report "n Out of range". + + CP #F1 ; + JP NC,L0F12 ; Jump if 241 or above to produce error report "n Out of range". + +;A holds a value in the range 60 to 240 + + LD A,(IX+#02) ; Fetch the channel number. + OR A ; Tempo 'T' commands have to be specified in the first string. + RET NZ ; If it is in a later string then ignore it. + + LD B,#00 ; [Redundant instruction - B is already zero] + PUSH BC ; C=Tempo value. + POP HL ; + ADD HL,HL ; + ADD HL,HL ; HL=Tempo*4. + + PUSH HL ; + POP BC ; BC=Tempo*4. [Would have been quicker to use the combination LD B,H and LD C,L] + + PUSH IY ; Save the pointer to the play command data block. + RST #28 ; + DEFW STACK_BC ; #2D2B. Place the contents of BC onto the stack. The call restores IY to #5C3A. + DI ; Interrupts get re-enabled by the call mechanism to ROM 1 so disable them again. + POP IY ; Restore IY to point at the play command data block. + + PUSH IY ; Save the pointer to the play command data block. + + PUSH IY ; + POP HL ; HL=pointer to the play command data block. + + LD BC,#002B ; + ADD HL,BC ; HL =IY+#002B. + LD IY,#5C3A ; Reset IY to #5C3A since this is required by the floating point calculator. + PUSH HL ; HL=Points to the calculator RAM routine. + + LD HL,L0C76 ; + LD (RETADDR),HL ; #5B5A. Set up the return address. + + LD HL,YOUNGER ; + EX (SP),HL ; Stack the address of the swap routine used when returning to this ROM. + PUSH HL ; Re-stack the address of the calculator RAM routine. + + JP SWAP_ROM ; #5B00. Toggle to other ROM and make a return to the calculator RAM routine. + +; -------------------- +; Tempo Command Return +; -------------------- +; The calculator stack now holds the value (10/(Tempo*4))/7.33e-6 and this is stored as the tempo value. +; The result is used an inner loop counter in the wait routine at #0F76 (ROM 0). Each iteration of this loop +; takes 26 T-states. The time taken by 26 T-states is 7.33e-6 seconds. So the total time for the loop +; to execute is 2.5/TEMPO seconds. + +L0C76: DI ; Interrupts get re-enabled by the call mechanism to ROM 1 so disable them again. + + RST #28 ; + DEFW FP_TO_BC ; #2DA2. Fetch the value on the top of the calculator stack. + + DI ; Interrupts get re-enabled by the call mechanism to ROM 1 so disable them again. + + POP IY ; Restore IY to point at the play command data block. + + LD (IY+#27),C ; Store tempo timing value. + LD (IY+#28),B ; + RET ; + +; ------------------------ +; Play Command 'M' (Mixer) +; ------------------------ +; This command is used to select whether to use tone and/or noise on each of the 3 channels. +; It is followed by a numeric value in the range 1 to 63, although due to loose range checking the +; value MOD 256 only needs to be within 0 to 63. Hence M256 operates the same as M0. +; Entry: IX=Address of the channel data block. + +L0C84: CALL L0B1D ; Get following numeric value from the string into BC. + LD A,C ; A=Mixer value. + CP #40 ; Is it 64 or above? + JP NC,L0F12 ; Jump if so to produce error report "n Out of range". + +;Bit 0: 1=Enable channel A tone. +;Bit 1: 1=Enable channel B tone. +;Bit 2: 1=Enable channel C tone. +;Bit 3: 1=Enable channel A noise. +;Bit 4: 1=Enable channel B noise. +;Bit 5: 1=Enable channel C noise. + + CPL ; Invert the bits since the sound generator's mixer register uses active low enable. + ; This also sets bit 6 1, which selects the I/O port as an output. + LD E,A ; E=Mixer value. + LD D,#07 ; D=Register 7 - Mixer. + CALL L0E7C ; Write to sound generator register to set the mixer. + RET ; [Could have saved 1 byte by using JP #0E7C (ROM 0)] + +; ------------------------- +; Play Command 'V' (Volume) +; ------------------------- +; This sets the volume of a channel and is followed by a numeric value in the range +; 0 (minimum) to 15 (maximum), although due to loose range checking the value MOD 256 +; only needs to be within 0 to 15. Hence V256 operates the same as V0. +; Entry: IX=Address of the channel data block. + +L0C95: CALL L0B1D ; Get following numeric value from the string into BC. + + LD A,C ; + CP #10 ; Is it 16 or above? + JP NC,L0F12 ; Jump if so to produce error report "n Out of range". + + LD (IX+#04),A ; Store the volume level. + +; [*BUG* - An attempt to set the volume for a sound chip channel is now made. However, this routine fails to take into account +; that it is also called to set the volume for a MIDI only channel, i.e. play strings 4 to 8. As a result, corruption +; occurs to various sound generator registers, causing spurious sound output. There is in fact no need for this routine +; to set the volume for any channels since this is done every time a new note is played - see routine at #0A97 (ROM 0). +; the bug fix is to simply to make a return at this point. This routine therefore contains 11 surplus bytes. Credit: Ian Collier (+3), Paul Farrow (128)] + + LD E,(IX+#02) ; E=Channel number. + LD A,#08 ; Offset by 8. + ADD A,E ; A=8+index. + LD D,A ; D=Sound generator register number for the channel. + + LD E,C ; E=Volume level. + CALL L0E7C ; Write to sound generator register to set the volume for the channel. + RET ; [Could have saved 1 byte by using JP #0E7C (ROM 0)] + +; ------------------------------------ +; Play Command 'U' (Use Volume Effect) +; ------------------------------------ +; This command turns on envelope waveform effects for a particular sound chip channel. The volume level is now controlled by +; the selected envelope waveform for the channel, as defined by the 'W' command. MIDI channels do not support envelope waveforms +; and so the routine has the effect of setting the volume of a MIDI channel to maximum, i.e. 15. It might seem odd that the volume +; for MIDI channels is set to 15 rather than just filtered out. However, the three sound chip channels can also drive three MIDI +; channels and so it would be inconsistent for these MIDI channels to have their volume set to 15 but have the other MIDI channels +; behave differently. However, it could be argued that all MIDI channels should be unaffected by the 'U' command. +; There are no parameters to this command. +; Entry: IX=Address of the channel data block. + +L0CAD: LD E,(IX+#02) ; Get the channel number. + LD A,#08 ; Offset by 8. + ADD A,E ; A=8+index. + LD D,A ; D=Sound generator register number for the channel. [This is not used and so there is no need to generate it. It was probably a left + ; over from copying and modifying the 'V' command routine. Deleting it would save 7 bytes. Credit: Ian Collier (+3), Paul Farrow (128)] + + LD E,#1F ; E=Select envelope defined by register 13, and reset volume bits to maximum (though these are not used with the envelope). + LD (IX+#04),E ; Store that the envelope is being used (along with the reset volume level). + RET ; + +; ------------------------------------------ +; Play command 'W' (Volume Effect Specifier) +; ------------------------------------------ +; This command selects the envelope waveform to use and is followed by a numeric value in the range +; 0 to 7, although due to loose range checking the value MOD 256 only needs to be within 0 to 7. +; Hence W256 operates the same as W0. +; Entry: IX=Address of the channel data block. + +L0CBA: CALL L0B1D ; Get following numeric value from the string into BC. + + LD A,C ; + CP #08 ; Is it 8 or above? + JP NC,L0F12 ; Jump if so to produce error report "n Out of range". + + LD B,#00 ; + LD HL,L0DE8 ; Envelope waveform lookup table. + ADD HL,BC ; HL points to the corresponding value in the table. + LD A,(HL) ; + LD (IY+#29),A ; Store new effect waveform value. + RET ; + +; ----------------------------------------- +; Play Command 'X' (Volume Effect Duration) +; ----------------------------------------- +; This command allows the duration of a waveform effect to be specified, and is followed by a numeric +; value in the range 0 to 65535. A value of 1 corresponds to the minimum duration, increasing up to 65535 +; and then maximum duration for a value of 0. If no numeric value is specified then the maximum duration is used. +; Entry: IX=Address of the channel data block. + +L0CCE: CALL L0B1D ; Get following numeric value from the string into BC. + + LD D,#0B ; Register 11 - Envelope Period Fine. + LD E,C ; + CALL L0E7C ; Write to sound generator register to set the envelope period (low byte). + + INC D ; Register 12 - Envelope Period Coarse. + LD E,B ; + CALL L0E7C ; Write to sound generator register to set the envelope period (high byte). + RET ; [Could have saved 1 byte by using JP #0E7C (ROM 0)] + +; ------------------------------- +; Play Command 'Y' (MIDI Channel) +; ------------------------------- +; This command sets the MIDI channel number that the string is assigned to and is followed by a numeric +; value in the range 1 to 16, although due to loose range checking the value MOD 256 only needs to be within 1 to 16. +; Hence Y257 operates the same as Y1. +; Entry: IX=Address of the channel data block. + +L0CDD: CALL L0B1D ; Get following numeric value from the string into BC. + + LD A,C ; + DEC A ; Is it 0? + JP M,L0F12 ; Jump if so to produce error report "n Out of range". + + CP #10 ; Is it 10 or above? + JP NC,L0F12 ; Jump if so to produce error report "n Out of range". + + LD (IX+#01),A ; Store MIDI channel number that this string is assigned to. + RET ; + +; ---------------------------------------- +; Play Command 'Z' (MIDI Programming Code) +; ---------------------------------------- +; This command is used to send a programming code to the MIDI port. It is followed by a numeric +; value in the range 0 to 255, although due to loose range checking the value MOD 256 only needs +; to be within 0 to 255. Hence Z256 operates the same as Z0. +; Entry: IX=Address of the channel data block. + +L0CEE: CALL L0B1D ; Get following numeric value from the string into BC. + + LD A,C ; A=(low byte of) the value. + CALL L11A3 ; Write byte to MIDI device. + RET ; [Could have saved 1 byte by using JP #0E7C (ROM 0)] + +; ----------------------- +; Play Command 'H' (Stop) +; ----------------------- +; This command stops further processing of a play command. It has no parameters. +; Entry: IX=Address of the channel data block. + +L0CF6: LD (IY+#10),#FF ; Indicate no channels to play, thereby causing + RET ; the play command to terminate. + +; -------------------------------------------------------- +; Play Commands 'a'..'g', 'A'..'G', '1'.."12", '&' and '_' +; -------------------------------------------------------- +; This handler routine processes commands 'a'..'g', 'A'..'G', '1'.."12", '&' and '_', +; and determines the length of the next note to play. It provides the handling of triplet and tied notes. +; It stores the note duration in the channel data block's duration length entry, and sets a pointer in the command +; data block's duration lengths pointer table to point at it. A single note letter is deemed to be a tied +; note count of 1. Triplets are deemed a tied note count of at least 2. +; Entry: IX=Address of the channel data block. +; A=Current character from play string. + +L0CFB: CALL L0E19 ; Is the current character a number? + JP C,L0D81 ; Jump if not number digit. + +;The character is a number digit + + CALL L0DAC ; HL=Address of the duration length within the channel data block. + CALL L0DB4 ; Store address of duration length in command data block's channel duration length pointer table. + + XOR A ; + LD (IX+#21),A ; Set no tied notes. + + CALL L0EC8 ; Get the previous character in the string, the note duration. + CALL L0B1D ; Get following numeric value from the string into BC. + LD A,C ; + OR A ; Is the value 0? + JP Z,L0F12 ; Jump if so to produce error report "n Out of range". + + CP #0D ; Is it 13 or above? + JP NC,L0F12 ; Jump if so to produce error report "n Out of range". + + CP #0A ; Is it below 10? + JR C,L0D32 ; Jump if so. + +;It is a triplet semi-quaver (10), triplet quaver (11) or triplet crotchet (12) + + CALL L0E00 ; DE=Note duration length for the duration value. + CALL L0D74 ; Increment the tied notes counter. + LD (HL),E ; HL=Address of the duration length within the channel data block. + INC HL ; + LD (HL),D ; Store the duration length. + +L0D28: CALL L0D74 ; Increment the counter of tied notes. + + INC HL ; + LD (HL),E ; + INC HL ; Store the subsequent note duration length in the channel data block. + LD (HL),D ; + INC HL ; + JR L0D38 ; Jump ahead to continue. + +;The note duration was in the range 1 to 9 + +L0D32: LD (IX+#05),C ; C=Note duration value (1..9). + CALL L0E00 ; DE=Duration length for this duration value. + +L0D38: CALL L0D74 ; Increment the tied notes counter. + +L0D3B: CALL L0EE3 ; Get the current character from the play string for this channel. + + CP '_' ; #5F. Is it a tied note? + JR NZ,L0D6E ; Jump ahead if not. + + CALL L0AC5 ; Get the current character from the PLAY string, and advance the position pointer. + CALL L0B1D ; Get following numeric value from the string into BC. + LD A,C ; Place the value into A. + CP #0A ; Is it below 10? + JR C,L0D5F ; Jump ahead for 1 to 9 (semiquaver ... semibreve). + +;A triplet note was found as part of a tied note + + PUSH HL ; HL=Address of the duration length within the channel data block. + PUSH DE ; DE=First tied note duration length. + CALL L0E00 ; DE=Note duration length for this new duration value. + POP HL ; HL=Current tied note duration length. + ADD HL,DE ; HL=Current+new tied note duration lengths. + LD C,E ; + LD B,D ; BC=Note duration length for the duration value. + EX DE,HL ; DE=Current+new tied note duration lengths. + POP HL ; HL=Address of the duration length within the channel data block. + + LD (HL),E ; + INC HL ; + LD (HL),D ; Store the combined note duration length in the channel data block. + + LD E,C ; + LD D,B ; DE=Note duration length for the second duration value. + JR L0D28 ; Jump back. + +;A non-triplet tied note + +L0D5F: LD (IX+#05),C ; Store the note duration value. + + PUSH HL ; HL=Address of the duration length within the channel data block. + PUSH DE ; DE=First tied note duration length. + CALL L0E00 ; DE=Note duration length for this new duration value. + POP HL ; HL=Current tied note duration length. + ADD HL,DE ; HL=Current+new tied not duration lengths. + EX DE,HL ; DE=Current+new tied not duration lengths. + POP HL ; HL=Address of the duration length within the channel data block. + + JP L0D3B ; Jump back to process the next character in case it is also part of a tied note. + +;The number found was not part of a tied note, so store the duration value + +L0D6E: LD (HL),E ; HL=Address of the duration length within the channel data block. + INC HL ; (For triplet notes this could be the address of the subsequent note duration length) + LD (HL),D ; Store the duration length. + JP L0D9C ; Jump forward to make a return. + +; This subroutine is called to increment the tied notes counter + +L0D74: LD A,(IX+#21) ; Increment counter of tied notes. + INC A ; + CP #0B ; Has it reached 11? + JP Z,L0F3A ; Jump if so to produce to error report "o too many tied notes". + + LD (IX+#21),A ; Store the new tied notes counter. + RET ; + +;The character is not a number digit so is 'A'..'G', '&' or '_' + +L0D81: CALL L0EC8 ; Get the previous character from the string. + + LD (IX+#21),#01 ; Set the number of tied notes to 1. + +;Store a pointer to the channel data block's duration length into the command data block + + CALL L0DAC ; HL=Address of the duration length within the channel data block. + CALL L0DB4 ; Store address of duration length in command data block's channel duration length pointer table. + + LD C,(IX+#05) ; C=The duration value of the note (1 to 9). + PUSH HL ; [Not necessary] + CALL L0E00 ; Find the duration length for the note duration value. + POP HL ; [Not necessary] + + LD (HL),E ; Store it in the channel data block. + INC HL ; + LD (HL),D ; + JP L0D9C ; Jump to the instruction below. [Redundant instruction] + +L0D9C: POP HL ; + INC HL ; + INC HL ; Modify the return address to point to the RET instruction at #0B83 (ROM 0). + PUSH HL ; + RET ; [Over elaborate when a simple POP followed by RET would have sufficed, saving 3 bytes] + +; ------------------- +; End of String Found +; ------------------- +;This routine is called when the end of string is found within a comment. It marks the +;string as having been processed and then returns to the main loop to process the next string. + +L0DA1: POP HL ; Drop the return address of the call to the comment command. + +;Enter here if the end of the string is found whilst processing a string. + +L0DA2: LD A,(IY+#21) ; Fetch the channel selector. + OR (IY+#10) ; Clear the channel flag for this string. + LD (IY+#10),A ; Store the new channel bitmap. + RET ; + +; -------------------------------------------------- +; Point to Duration Length within Channel Data Block +; -------------------------------------------------- +; Entry: IX=Address of the channel data block. +; Exit : HL=Address of the duration length within the channel data block. + +L0DAC: PUSH IX ; + POP HL ; HL=Address of the channel data block. + LD BC,#0022 ; + ADD HL,BC ; HL=Address of the store for the duration length. + RET ; + +; ------------------------------------------------------------------------- +; Store Entry in Command Data Block's Channel Duration Length Pointer Table +; ------------------------------------------------------------------------- +; Entry: IY=Address of the command data block. +; IX=Address of the channel data block for the current string. +; HL=Address of the duration length store within the channel data block. +; Exit : HL=Address of the duration length store within the channel data block. +; DE=Channel duration. + +L0DB4: PUSH HL ; Save the address of the duration length within the channel data block. + + PUSH IY ; + POP HL ; HL=Address of the command data block. + + LD BC,#0011 ; + ADD HL,BC ; HL=Address within the command data block of the channel duration length pointer table. + + LD B,#00 ; + LD C,(IX+#02) ; BC=Channel number. + + SLA C ; BC=2*Index number. + ADD HL,BC ; HL=Address within the command data block of the pointer to the current channel's data block duration length. + + POP DE ; DE=Address of the duration length within the channel data block. + + LD (HL),E ; Store the pointer to the channel duration length in the command data block's channel duration pointer table. + INC HL ; + LD (HL),D ; + EX DE,HL ; + RET ; + +; ----------------------- +; PLAY Command Jump Table +; ----------------------- +; Handler routine jump table for all PLAY commands. + +L0DCA: DEFW L0CFB ; Command handler routine for all other characters. + DEFW L0B85 ; '!' command handler routine. + DEFW L0B90 ; 'O' command handler routine. + DEFW L0BA5 ; 'N' command handler routine. + DEFW L0BA6 ; '(' command handler routine. + DEFW L0BC2 ; ')' command handler routine. + DEFW L0C32 ; 'T' command handler routine. + DEFW L0C84 ; 'M' command handler routine. + DEFW L0C95 ; 'V' command handler routine. + DEFW L0CAD ; 'U' command handler routine. + DEFW L0CBA ; 'W' command handler routine. + DEFW L0CCE ; 'X' command handler routine. + DEFW L0CDD ; 'Y' command handler routine. + DEFW L0CEE ; 'Z' command handler routine. + DEFW L0CF6 ; 'H' command handler routine. + +; ------------------------------ +; Envelope Waveform Lookup Table +; ------------------------------ +; Table used by the play 'W' command to find the corresponding envelope value +; to write to the sound generator envelope shape register (register 13). This +; filters out the two duplicate waveforms possible from the sound generator and +; allows the order of the waveforms to be arranged in a more logical fashion. + +L0DE8: DEFB #00 ; W0 - Single decay then off. (Continue off, attack off, alternate off, hold off) + DEFB #04 ; W1 - Single attack then off. (Continue off, attack on, alternate off, hold off) + DEFB #0B ; W2 - Single decay then hold. (Continue on, attack off, alternate on, hold on) + DEFB #0D ; W3 - Single attack then hold. (Continue on, attack on, alternate off, hold on) + DEFB #08 ; W4 - Repeated decay. (Continue on, attack off, alternate off, hold off) + DEFB #0C ; W5 - Repeated attack. (Continue on, attack on, alternate off, hold off) + DEFB #0E ; W6 - Repeated attack-decay. (Continue on, attack on, alternate on, hold off) + DEFB #0A ; W7 - Repeated decay-attack. (Continue on, attack off, alternate on, hold off) + +; -------------------------- +; Identify Command Character +; -------------------------- +; This routines attempts to match the command character to those in a table. +; The index position of the match indicates which command handler routine is required +; to process the character. Note that commands are case sensitive. +; Entry: A=Command character. +; Exit : Zero flag set if a match was found. +; BC=Indentifying the character matched, 1 to 15 for match and 0 for no match. + +L0DF0: LD BC,#000F ; Number of characters + 1 in command table. + LD HL,L0AB7 ; Start of command table. + CPIR ; Search for a match. + RET ; + +; --------------- +; Semitones Table +; --------------- +; This table contains an entry for each note of the scale, A to G, +; and is the number of semitones above the note C. + +L0DF9: DEFB #09 ; 'A' + DEFB #0B ; 'B' + DEFB #00 ; 'C' + DEFB #02 ; 'D' + DEFB #04 ; 'E' + DEFB #05 ; 'F' + DEFB #07 ; 'G' + +; ------------------------- +; Find Note Duration Length +; ------------------------- +; Entry: C=Duration value (0 to 12, although a value of 0 is never used). +; Exit : DE=Note duration length. + +L0E00: PUSH HL ; Save HL. + + LD B,#00 ; + LD HL,L0E0C ; Note duration table. + ADD HL,BC ; Index into the table. + LD D,#00 ; + LD E,(HL) ; Fetch the length from the table. + + POP HL ; Restore HL. + RET ; + +; ------------------- +; Note Duration Table +; ------------------- +; A whole note is given by a value of 96d and other notes defined in relation to this. +; The value of 96d is the lowest common denominator from which all note durations +; can be defined. + +L0E0C: DEFB #80 ; Rest [Not used since table is always indexed into with a value of 1 or more] + DEFB #06 ; Semi-quaver (sixteenth note). + DEFB #09 ; Dotted semi-quaver (3/32th note). + DEFB #0C ; Quaver (eighth note). + DEFB #12 ; Dotted quaver (3/16th note). + DEFB #18 ; Crotchet (quarter note). + DEFB #24 ; Dotted crotchet (3/8th note). + DEFB #30 ; Minim (half note). + DEFB #48 ; Dotted minim (3/4th note). + DEFB #60 ; Semi-breve (whole note). + DEFB #04 ; Triplet semi-quaver (1/24th note). + DEFB #08 ; Triplet quaver (1/12th note). + DEFB #10 ; Triplet crochet (1/6th note). + +; ----------------- +; Is Numeric Digit? +; ----------------- +; Tests whether a character is a number digit. +; Entry: A=Character. +; Exit : Carry flag reset if a number digit. + +L0E19: CP '0' ; #30. Is it '0' or less? + RET C ; Return with carry flag set if so. + + CP ':' ; #3A. Is it more than '9'? + CCF ; + RET ; Return with carry flag set if so. + +; ----------------------------------- +; Play a Note On a Sound Chip Channel +; ----------------------------------- +; This routine plays the note at the current octave and current volume on a sound chip channel. For play strings 4 to 8, +; it simply stores the note number and this is subsequently played later. +; Entry: IX=Address of the channel data block. +; A=Note value as number of semitones above C (0..11). + +L0E20: LD C,A ; C=The note value. + LD A,(IX+#03) ; Octave number * 12. + ADD A,C ; Add the octave number and the note value to form the note number. + CP #80 ; Is note within range? + JP NC,L0F32 ; Jump if not to produce error report "m Note out of range". + + LD C,A ; C=Note number. + LD A,(IX+#02) ; Get the channel number. + OR A ; Is it the first channel? + JR NZ,L0E3F ; Jump ahead if not. + +;Only set the noise generator frequency on the first channel + + LD A,C ; A=Note number (0..107), in ascending audio frequency. + CPL ; Invert since noise register value is in descending audio frequency. + AND #7F ; Mask off bit 7. + SRL A ; + SRL A ; Divide by 4 to reduce range to 0..31. + LD D,#06 ; Register 6 - Noise pitch. + LD E,A ; + CALL L0E7C ; Write to sound generator register. + +L0E3F: LD (IX+#00),C ; Store the note number. + LD A,(IX+#02) ; Get the channel number. + CP #03 ; Is it channel 0, 1 or 2, i.e. a sound chip channel? + RET NC ; Do not output anything for play strings 4 to 8. + +;Channel 0, 1 or 2 + + LD HL,L1096 ; Start of note lookup table. + LD B,#00 ; BC=Note number. + LD A,C ; A=Note number. + SUB #15 ; A=Note number - 21. + JR NC,L0E57 ; Jump if note number was 21 or above. + + LD DE,#0FBF ; Note numbers #00 to #14 use the lowest note value. + JR L0E5E ; [Could have saved 4 bytes by using XOR A and dropping through to #0E57 (ROM 0)] + +;Note number 21 to 107 (range 0 to 86) + +L0E57: LD C,A ; + SLA C ; Generate offset into the table. + ADD HL,BC ; Point to the entry in the table. + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Word to write to the sound chip registers to produce this note. + +L0E5E: EX DE,HL ; HL=Register word value to produce the note. + + LD D,(IX+#02) ; Get the channel number. + SLA D ; D=2*Channel number, to give the tone channel register (fine control) number 0, 2, or 4. + LD E,L ; E=The low value byte. + CALL L0E7C ; Write to sound generator register. + + INC D ; D=Tone channel register (coarse control) number 1, 3, or 5. + LD E,H ; E=The high value byte. + CALL L0E7C ; Write to sound generator register. + + BIT 4,(IX+#04) ; Is the envelope waveform being used? + RET Z ; Return if it is not. + + LD D,#0D ; Register 13 - Envelope Shape. + LD A,(IY+#29) ; Get the effect waveform value. + LD E,A ; + CALL L0E7C ; Write to sound generator register. + RET ; [Could have saved 4 bytes by dropping down into the routine below.] + +; ---------------------------- +; Set Sound Generator Register +; ---------------------------- +; Entry: D=Register to write. +; E=Value to set register to. + +L0E7C: PUSH BC ; + + LD BC,#FFFD ; + OUT (C),D ; Select the register. + LD BC,#BFFD ; + OUT (C),E ; Write out the value. + + POP BC ; + RET ; + +; ----------------------------- +; Read Sound Generator Register +; ----------------------------- +; Entry: A=Register to read. +; Exit : A=Value of currently selected sound generator register. + +L0E89: PUSH BC ; + + LD BC,#FFFD ; + OUT (C),A ; Select the register. + IN A,(C) ; Read the register's value. + + POP BC ; + RET ; + +; ------------------ +; Turn Off All Sound +; ------------------ + +L0E93: LD D,#07 ; Register 7 - Mixer. + LD E,#FF ; I/O ports are inputs, noise output off, tone output off. + CALL L0E7C ; Write to sound generator register. + +;Turn off the sound from the AY-3-8912 + + LD D,#08 ; Register 8 - Channel A volume. + LD E,#00 ; Volume of 0. + CALL L0E7C ; Write to sound generator register to set the volume to 0. + + INC D ; Register 9 - Channel B volume. + CALL L0E7C ; Write to sound generator register to set the volume to 0. + + INC D ; Register 10 - Channel C volume. + CALL L0E7C ; Write to sound generator register to set the volume to 0. + + CALL L0A4F ; Select channel data block pointers. + +;Now reset all MIDI channels in use + +L0EAC: RR (IY+#22) ; Working copy of channel bitmap. Test if next string present. + JR C,L0EB8 ; Jump ahead if there is no string for this channel. + + CALL L0A67 ; Get address of channel data block for the current string into IX. + CALL L118D ; Turn off the MIDI channel sound assigned to this play string. + +L0EB8: SLA (IY+#21) ; Have all channels been processed? + JR C,L0EC3 ; Jump ahead if so. + + CALL L0A6E ; Advance to the next channel data block pointer. + JR L0EAC ; Jump back to process the next channel. + +L0EC3: LD IY,#5C3A ; Restore IY. + RET ; + +; --------------------------------------- +; Get Previous Character from Play String +; --------------------------------------- +; Get the previous character from the PLAY string, skipping over spaces and 'Enter' characters. +; Entry: IX=Address of the channel data block. + +L0EC8: PUSH HL ; Save registers. + PUSH DE ; + + LD L,(IX+#06) ; Get the current pointer into the PLAY string. + LD H,(IX+#07) ; + +L0ED0: DEC HL ; Point to previous character. + LD A,(HL) ; Fetch the character. + CP ' ' ; #20. Is it a space? + JR Z,L0ED0 ; Jump back if a space. + + CP #0D ; Is it an 'Enter'? + JR Z,L0ED0 ; Jump back if an 'Enter'. + + LD (IX+#06),L ; Store this as the new current pointer into the PLAY string. + LD (IX+#07),H ; + + POP DE ; Restore registers. + POP HL ; + RET ; + +; -------------------------------------- +; Get Current Character from Play String +; -------------------------------------- +; Get the current character from the PLAY string, skipping over spaces and 'Enter' characters. +; Exit: Carry flag set if string has been fully processed. +; Carry flag reset if character is available. +; A=Character available. + +L0EE3: PUSH HL ; Save registers. + PUSH DE ; + PUSH BC ; + + LD L,(IX+#06) ; HL=Pointer to next character to process within the PLAY string. + LD H,(IX+#07) ; + +L0EEC: LD A,H ; + CP (IX+#09) ; Reached end-of-string address high byte? + JR NZ,L0EFB ; Jump forward if not. + + LD A,L ; + CP (IX+#08) ; Reached end-of-string address low byte? + JR NZ,L0EFB ; Jump forward if not. + + SCF ; Indicate string all processed. + JR L0F05 ; Jump forward to return. + +L0EFB: LD A,(HL) ; Get the next play character. + CP ' ' ; #20. Is it a space? + JR Z,L0F09 ; Ignore the space by jumping ahead to process the next character. + + CP #0D ; Is it 'Enter'? + JR Z,L0F09 ; Ignore the 'Enter' by jumping ahead to process the next character. + + OR A ; Clear the carry flag to indicate a new character has been returned. + +L0F05: POP BC ; Restore registers. + POP DE ; + POP HL ; + RET ; + +L0F09: INC HL ; Point to the next character. + LD (IX+#06),L ; + LD (IX+#07),H ; Update the pointer to the next character to process with the PLAY string. + JR L0EEC ; Jump back to get the next character. + +; -------------------------- +; Produce Play Error Reports +; -------------------------- + +L0F12: CALL L0E93 ; Turn off all sound and restore IY. + EI ; + CALL L05AC ; Produce error report. + DEFB #29 ; "n Out of range" + +L0F1A: CALL L0E93 ; Turn off all sound and restore IY. + EI ; + CALL L05AC ; Produce error report. + DEFB #27 ; "l Number too big" + +L0F22: CALL L0E93 ; Turn off all sound and restore IY. + EI ; + CALL L05AC ; Produce error report. + DEFB #26 ; "k Invalid note name" + +L0F2A: CALL L0E93 ; Turn off all sound and restore IY. + EI ; + CALL L05AC ; Produce error report. + DEFB #1F ; "d Too many brackets" + +L0F32: CALL L0E93 ; Turn off all sound and restore IY. + EI ; + CALL L05AC ; Produce error report. + DEFB #28 ; "m Note out of range" + +L0F3A: CALL L0E93 ; Turn off all sound and restore IY. + EI ; + CALL L05AC ; Produce error report. + DEFB #2A ; "o Too many tied notes" + +; ------------------------- +; Play Note on Each Channel +; ------------------------- +; Play a note and set the volume on each channel for which a play string exists. + +L0F42: CALL L0A4F ; Select channel data block pointers. + +L0F45: RR (IY+#22) ; Working copy of channel bitmap. Test if next string present. + JR C,L0F6C ; Jump ahead if there is no string for this channel. + + CALL L0A67 ; Get address of channel data block for the current string into IX. + + CALL L0AD1 ; Get the next note in the string as number of semitones above note C. + CP #80 ; Is it a rest? + JR Z,L0F6C ; Jump ahead if so and do nothing to the channel. + + CALL L0E20 ; Play the note if a sound chip channel. + + LD A,(IX+#02) ; Get channel number. + CP #03 ; Is it channel 0, 1 or 2, i.e. a sound chip channel? + JR NC,L0F69 ; Jump if not to skip setting the volume. + +;One of the 3 sound chip generator channels so set the channel's volume for the new note + + LD D,#08 ; + ADD A,D ; A=0 to 2. + LD D,A ; D=Register (8 + string index), i.e. channel A, B or C volume register. + LD E,(IX+#04) ; E=Volume for the current channel. + CALL L0E7C ; Write to sound generator register to set the output volume. + +L0F69: CALL L116E ; Play a note and set the volume on the assigned MIDI channel. + +L0F6C: SLA (IY+#21) ; Have all channels been processed? + RET C ; Return if so. + + CALL L0A6E ; Advance to the next channel data block pointer. + JR L0F45 ; Jump back to process the next channel. + +; ------------------ +; Wait Note Duration +; ------------------ +; This routine is the main timing control of the PLAY command. +; It waits for the specified length of time, which will be the +; lowest note duration of all active channels. +; The actual duration of the wait is dictated by the current tempo. +; Entry: DE=Note duration, where 96d represents a whole note. + +;Enter a loop waiting for (135+ ((26*(tempo-100))-5) )*DE+5 T-states + +L0F76: PUSH HL ; (11) Save HL. + + LD L,(IY+#27) ; (19) Get the tempo timing value. + LD H,(IY+#28) ; (19) + + LD BC,#0064 ; (10) BC=100 + OR A ; (4) + SBC HL,BC ; (15) HL=tempo timing value - 100. + + PUSH HL ; (11) + POP BC ; (10) BC=tempo timing value - 100. + + POP HL ; (10) Restore HL. + +;Tempo timing value = (10/(TEMPO*4))/7.33e-6, where 7.33e-6 is the time for 26 T-states. +;The loop below takes 26 T-states per iteration, where the number of iterations is given by the tempo timing value. +;So the time for the loop to execute is 2.5/TEMPO seconds. +; +;For a TEMPO of 60 beats (crotchets) per second, the time per crotchet is 1/24 second. +;The duration of a crotchet is defined as 24 from the table at #0E0C, therefore the loop will get executed 24 times +;and hence the total time taken will be 1 second. +; +;The tempo timing value above has 100 subtracted from it, presumably to approximately compensate for the overhead time +;previously taken to prepare the notes for playing. This reduces the total time by 2600 T-states, or 733us. + +L0F86: DEC BC ; (6) Wait for tempo-100 loops. + LD A,B ; (4) + OR C ; (4) + JR NZ,L0F86 ; (12/7) + + DEC DE ; (6) Repeat DE times + LD A,D ; (4) + OR E ; (4) + JR NZ,L0F76 ; (12/7) + + RET ; (10) + +; ----------------------------- +; Find Smallest Duration Length +; ----------------------------- +; This routine finds the smallest duration length for all current notes +; being played across all channels. +; Exit: DE=Smallest duration length. + +L0F91: LD DE,#FFFF ; Set smallest duration length to 'maximum'. + + CALL L0A4A ; Select channel data block duration pointers. + +L0F97: RR (IY+#22) ; Working copy of channel bitmap. Test if next string present. + JR C,L0FAF ; Jump ahead if there is no string for this channel. + +;HL=Address of channel data pointer. DE holds the smallest duration length found so far. + + PUSH DE ; Save the smallest duration length. + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; + EX DE,HL ; DE=Channel data block duration length. + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Channel duration length. + + PUSH DE ; + POP HL ; HL=Channel duration length. + + POP BC ; Last channel duration length. + OR A ; + SBC HL,BC ; Is current channel's duration length smaller than the smallest so far? + JR C,L0FAF ; Jump ahead if so, with the new smallest value in DE. + +;The current channel's duration was not smaller so restore the last smallest into DE. + + PUSH BC ; + POP DE ; DE=Smallest duration length. + +L0FAF: SLA (IY+#21) ; Have all channel strings been processed? + JR C,L0FBA ; Jump ahead if so. + + CALL L0A6E ; Advance to the next channel data block duration pointer. + JR L0F97 ; Jump back to process the next channel. + +L0FBA: LD (IY+#25),E ; + LD (IY+#26),D ; Store the smallest channel duration length. + RET ; + +; --------------------------------------------------------------- +; Play a Note on Each Channel and Update Channel Duration Lengths +; --------------------------------------------------------------- +; This routine is used to play a note and set the volume on all channels. +; It subtracts an amount of time from the duration lengths of all currently +; playing channel note durations. The amount subtracted is equivalent to the +; smallest note duration length currently being played, and as determined earlier. +; Hence one channel's duration will go to 0 on each call of this routine, and the +; others will show the remaining lengths of their corresponding notes. +; Entry: IY=Address of the command data block. + +L0FC1: XOR A ; + LD (IY+#2A),A ; Holds a temporary channel bitmap. + + CALL L0A4F ; Select channel data block pointers. + +L0FC8: RR (IY+#22) ; Working copy of channel bitmap. Test if next string present. + JP C,L105A ; Jump ahead if there is no string for this channel. + + CALL L0A67 ; Get address of channel data block for the current string into IX. + + PUSH IY ; + POP HL ; HL=Address of the command data block. + + LD BC,#0011 ; + ADD HL,BC ; HL=Address of channel data block duration pointers. + + LD B,#00 ; + LD C,(IX+#02) ; BC=Channel number. + SLA C ; BC=2*Channel number. + ADD HL,BC ; HL=Address of channel data block duration pointer for this channel. + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Address of duration length within the channel data block. + + EX DE,HL ; HL=Address of duration length within the channel data block. + PUSH HL ; Save it. + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Duration length for this channel. + + EX DE,HL ; HL=Duration length for this channel. + + LD E,(IY+#25) ; + LD D,(IY+#26) ; DE=Smallest duration length of all current channel notes. + + OR A ; + SBC HL,DE ; HL=Duration length - smallest duration length. + EX DE,HL ; DE=Duration length - smallest duration length. + + POP HL ; HL=Address of duration length within the channel data block. + JR Z,L0FFC ; Jump if this channel uses the smallest found duration length. + + LD (HL),E ; + INC HL ; Update the duration length for this channel with the remaining length. + LD (HL),D ; + + JR L105A ; Jump ahead to update the next channel. + +;The current channel uses the smallest found duration length + +; [A note has been completed and so the channel volume is set to 0 prior to the next note being played. +; This occurs on both sound chip channels and MIDI channels. When a MIDI channel is assigned to more than +; one play string and a rest is used in one of those strings. As soon as the end of the rest period is +; encountered, the channel's volume is set to off even though one of the other play strings controlling +; the MIDI channel may still be playing. This can be seen using the command PLAY "Y1a&", "Y1N9a". Here, +; string 1 starts playing 'a' for the period of a crotchet (1/4 of a note), where as string 2 starts playing +; 'a' for nine periods of a crotchet (9/4 of a note). When string 1 completes its crotchet, it requests +; to play a period of silence via the rest '&'. This turns the volume of the MIDI channel off even though +; string 2 is still timing its way through its nine crotchets. The play command will therefore continue for +; a further seven crotchets but in silence. This is because the volume for note is set only at its start +; and no coordination occurs between strings to turn the volume back on for the second string. It is arguably +; what the correct behaviour should be in such a circumstance where the strings are providing conflicting instructions, +; but having the latest command or note take precedence seems a logical approach. Credit: Ian Collier (+3), Paul Farrow (128)] + +L0FFC: LD A,(IX+#02) ; Get the channel number. + CP #03 ; Is it channel 0, 1 or 2, i.e. a sound chip channel? + JR NC,L100C ; Jump ahead if not a sound generator channel. + + LD D,#08 ; + ADD A,D ; + LD D,A ; D=Register (8+channel number) - Channel volume. + LD E,#00 ; E=Volume level of 0. + CALL L0E7C ; Write to sound generator register to turn the volume off. + +L100C: CALL L118D ; Turn off the assigned MIDI channel sound. + + PUSH IX ; + POP HL ; HL=Address of channel data block. + + LD BC,#0021 ; + ADD HL,BC ; HL=Points to the tied notes counter. + + DEC (HL) ; Decrement the tied notes counter. [This contains a value of 1 for a single note] + JR NZ,L1026 ; Jump ahead if there are more tied notes. + + CALL L0B5C ; Find the next note to play for this channel from its play string. + + LD A,(IY+#21) ; Fetch the channel selector. + AND (IY+#10) ; Test whether this channel has further data in its play string. + JR NZ,L105A ; Jump to process the next channel if this channel does not have a play string. + + JR L103D ; The channel has more data in its play string so jump ahead. + +;The channel has more tied notes + +L1026: PUSH IY ; + POP HL ; HL=Address of the command data block. + + LD BC,#0011 ; + ADD HL,BC ; HL=Address of channel data block duration pointers. + + LD B,#00 ; + LD C,(IX+#02) ; BC=Channel number. + SLA C ; BC=2*Channel number. + ADD HL,BC ; HL=Address of channel data block duration pointer for this channel. + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Address of duration length within the channel data block. + + INC DE ; + INC DE ; Point to the subsequent note duration length. + + LD (HL),D ; + DEC HL ; + LD (HL),E ; Store the new duration length. + +L103D: CALL L0AD1 ; Get next note in the string as number of semitones above note C. + LD C,A ; C=Number of semitones. + + LD A,(IY+#21) ; Fetch the channel selector. + AND (IY+#10) ; Test whether this channel has a play string. + JR NZ,L105A ; Jump to process the next channel if this channel does not have a play string. + + LD A,C ; A=Number of semitones. + CP #80 ; Is it a rest? + JR Z,L105A ; Jump to process the next channel if it is. + + CALL L0E20 ; Play the new note on this channel at the current volume if a sound chip channel, or simply store the note for play strings 4 to 8. + + LD A,(IY+#21) ; Fetch the channel selector. + OR (IY+#2A) ; Insert a bit in the temporary channel bitmap to indicate this channel has more to play. + LD (IY+#2A),A ; Store it. + +;Check whether another channel needs its duration length updated + +L105A: SLA (IY+#21) ; Have all channel strings been processed? + JR C,L1066 ; Jump ahead if so. + + CALL L0A6E ; Advance to the next channel data pointer. + JP L0FC8 ; Jump back to update the duration length for the next channel. + +; [*BUG* - By this point, the volume for both sound chip and MIDI channels has been set to 0, i.e. off. So although the new notes have been +; set playing on the sound chip channels, no sound is audible. For MIDI channels, no new notes have yet been output and hence these +; are also silent. If the time from turning the volume off for the current note to the time to turn the volume on for the next note +; is short enough, then it will not be noticeable. However, the code at #1066 (ROM 0) introduces a 1/96th of a note delay and as a result a +; 1/96th of a note period of silence between notes. The bug can be resolved by simply deleting the two instructions below that introduce +; the delay. A positive side effect of the bug in the 'V' volume command at #0C95 (ROM 0) is that it can be used to overcome the gaps of silence +; between notes for sound chip channels. By interspersing volume commands between notes, a new volume level is immediately set before +; the 1/96th of a note delay is introduced for the new note. Therefore, the delay occurs when the new note is audible instead of when it +; is silent. For example, PLAY "cV15cV15c" instead of PLAY "ccc". The note durations are still 1/96th of a note longer than they should +; be though. This technique will only work on the sound chip channels and not for any MIDI channels. Credit: Ian Collier (+3), Paul Farrow (128)] + +L1066: LD DE,#0001 ; Delay for 1/96th of a note. + CALL L0F76 ; + + CALL L0A4F ; Select channel data block pointers. + +;All channel durations have been updated. Update the volume on each sound chip channel, and the volume and note on each MIDI channel + +L106F: RR (IY+#2A) ; Temporary channel bitmap. Test if next string present. + JR NC,L108C ; Jump ahead if there is no string for this channel. + + CALL L0A67 ; Get address of channel data block for the current string into IX. + + LD A,(IX+#02) ; Get the channel number. + CP #03 ; Is it channel 0, 1 or 2, i.e. a sound chip channel? + JR NC,L1089 ; Jump ahead if so to process the next channel. + + LD D,#08 ; + ADD A,D ; + LD D,A ; D=Register (8+channel number) - Channel volume. + LD E,(IX+#04) ; Get the current volume. + CALL L0E7C ; Write to sound generator register to set the volume of the channel. + +L1089: CALL L116E ; Play a note and set the volume on the assigned MIDI channel. + +L108C: SLA (IY+#21) ; Have all channels been processed? + RET C ; Return if so. + + CALL L0A6E ; Advance to the next channel data pointer. + JR L106F ; Jump back to process the next channel. + +; ----------------- +; Note Lookup Table +; ----------------- +; Each word gives the value of the sound generator tone registers for a given note. +; There are 9 octaves, containing a total of 108 notes. These represent notes 21 to +; 128. Notes 0 to 20 cannot be reproduced on the sound chip and so note 21 will be +; used for all of these (they will however be sent to a MIDI device if one is assigned +; to a channel). [Note that both the sound chip and the MIDI port can not play note 128 +; and so its inclusion in the table is a waste of 2 bytes]. The PLAY command does not allow +; octaves higher than 8 to be selected directly. Using PLAY "O8G" will select note 115. To +; select higher notes, sharps must be included, e.g. PLAY "O8#G" for note 116, PLAY "O8##G" +; for note 117, etc, up to PLAY "O8############G" for note 127. Attempting to access note +; 128 using PLAY "O8#############G" will lead to error report "m Note out of range". + +L1096: DEFW #0FBF ; Octave 1, Note 21 - A (27.50 Hz, Ideal=27.50 Hz, Error=-0.01%) C0 + DEFW #0EDC ; Octave 1, Note 22 - A# (29.14 Hz, Ideal=29.16 Hz, Error=-0.08%) + DEFW #0E07 ; Octave 1, Note 23 - B (30.87 Hz, Ideal=30.87 Hz, Error=-0.00%) + + DEFW #0D3D ; Octave 2, Note 24 - C (32.71 Hz, Ideal=32.70 Hz, Error=+0.01%) C1 + DEFW #0C7F ; Octave 2, Note 25 - C# (34.65 Hz, Ideal=34.65 Hz, Error=-0.00%) + DEFW #0BCC ; Octave 2, Note 26 - D (36.70 Hz, Ideal=36.71 Hz, Error=-0.01%) + DEFW #0B22 ; Octave 2, Note 27 - D# (38.89 Hz, Ideal=38.89 Hz, Error=+0.01%) + DEFW #0A82 ; Octave 2, Note 28 - E (41.20 Hz, Ideal=41.20 Hz, Error=+0.00%) + DEFW #09EB ; Octave 2, Note 29 - F (43.66 Hz, Ideal=43.65 Hz, Error=+0.00%) + DEFW #095D ; Octave 2, Note 30 - F# (46.24 Hz, Ideal=46.25 Hz, Error=-0.02%) + DEFW #08D6 ; Octave 2, Note 31 - G (49.00 Hz, Ideal=49.00 Hz, Error=+0.00%) + DEFW #0857 ; Octave 2, Note 32 - G# (51.92 Hz, Ideal=51.91 Hz, Error=+0.01%) + DEFW #07DF ; Octave 2, Note 33 - A (55.01 Hz, Ideal=55.00 Hz, Error=+0.01%) + DEFW #076E ; Octave 2, Note 34 - A# (58.28 Hz, Ideal=58.33 Hz, Error=-0.08%) + DEFW #0703 ; Octave 2, Note 35 - B (61.75 Hz, Ideal=61.74 Hz, Error=+0.02%) + + DEFW #069F ; Octave 3, Note 36 - C ( 65.39 Hz, Ideal= 65.41 Hz, Error=-0.02%) C2 + DEFW #0640 ; Octave 3, Note 37 - C# ( 69.28 Hz, Ideal= 69.30 Hz, Error=-0.04%) + DEFW #05E6 ; Octave 3, Note 38 - D ( 73.40 Hz, Ideal= 73.42 Hz, Error=-0.01%) + DEFW #0591 ; Octave 3, Note 39 - D# ( 77.78 Hz, Ideal= 77.78 Hz, Error=+0.01%) + DEFW #0541 ; Octave 3, Note 40 - E ( 82.41 Hz, Ideal= 82.41 Hz, Error=+0.00%) + DEFW #04F6 ; Octave 3, Note 41 - F ( 87.28 Hz, Ideal= 87.31 Hz, Error=-0.04%) + DEFW #04AE ; Octave 3, Note 42 - F# ( 92.52 Hz, Ideal= 92.50 Hz, Error=+0.02%) + DEFW #046B ; Octave 3, Note 43 - G ( 98.00 Hz, Ideal= 98.00 Hz, Error=+0.00%) + DEFW #042C ; Octave 3, Note 44 - G# (103.78 Hz, Ideal=103.83 Hz, Error=-0.04%) + DEFW #03F0 ; Octave 3, Note 45 - A (109.96 Hz, Ideal=110.00 Hz, Error=-0.04%) + DEFW #03B7 ; Octave 3, Note 46 - A# (116.55 Hz, Ideal=116.65 Hz, Error=-0.08%) + DEFW #0382 ; Octave 3, Note 47 - B (123.43 Hz, Ideal=123.47 Hz, Error=-0.03%) + + DEFW #034F ; Octave 4, Note 48 - C (130.86 Hz, Ideal=130.82 Hz, Error=+0.04%) C3 + DEFW #0320 ; Octave 4, Note 49 - C# (138.55 Hz, Ideal=138.60 Hz, Error=-0.04%) + DEFW #02F3 ; Octave 4, Note 50 - D (146.81 Hz, Ideal=146.83 Hz, Error=-0.01%) + DEFW #02C8 ; Octave 4, Note 51 - D# (155.68 Hz, Ideal=155.55 Hz, Error=+0.08%) + DEFW #02A1 ; Octave 4, Note 52 - E (164.70 Hz, Ideal=164.82 Hz, Error=-0.07%) + DEFW #027B ; Octave 4, Note 53 - F (174.55 Hz, Ideal=174.62 Hz, Error=-0.04%) + DEFW #0257 ; Octave 4, Note 54 - F# (185.04 Hz, Ideal=185.00 Hz, Error=+0.02%) + DEFW #0236 ; Octave 4, Note 55 - G (195.83 Hz, Ideal=196.00 Hz, Error=-0.09%) + DEFW #0216 ; Octave 4, Note 56 - G# (207.57 Hz, Ideal=207.65 Hz, Error=-0.04%) + DEFW #01F8 ; Octave 4, Note 57 - A (219.92 Hz, Ideal=220.00 Hz, Error=-0.04%) + DEFW #01DC ; Octave 4, Note 58 - A# (232.86 Hz, Ideal=233.30 Hz, Error=-0.19%) + DEFW #01C1 ; Octave 4, Note 59 - B (246.86 Hz, Ideal=246.94 Hz, Error=-0.03%) + + DEFW #01A8 ; Octave 5, Note 60 - C (261.42 Hz, Ideal=261.63 Hz, Error=-0.08%) C4 Middle C + DEFW #0190 ; Octave 5, Note 61 - C# (277.10 Hz, Ideal=277.20 Hz, Error=-0.04%) + DEFW #0179 ; Octave 5, Note 62 - D (294.01 Hz, Ideal=293.66 Hz, Error=+0.12%) + DEFW #0164 ; Octave 5, Note 63 - D# (311.35 Hz, Ideal=311.10 Hz, Error=+0.08%) + DEFW #0150 ; Octave 5, Note 64 - E (329.88 Hz, Ideal=329.63 Hz, Error=+0.08%) + DEFW #013D ; Octave 5, Note 65 - F (349.65 Hz, Ideal=349.23 Hz, Error=+0.12%) + DEFW #012C ; Octave 5, Note 66 - F# (369.47 Hz, Ideal=370.00 Hz, Error=-0.14%) + DEFW #011B ; Octave 5, Note 67 - G (391.66 Hz, Ideal=392.00 Hz, Error=-0.09%) + DEFW #010B ; Octave 5, Note 68 - G# (415.13 Hz, Ideal=415.30 Hz, Error=-0.04%) + DEFW #00FC ; Octave 5, Note 69 - A (439.84 Hz, Ideal=440.00 Hz, Error=-0.04%) + DEFW #00EE ; Octave 5, Note 70 - A# (465.72 Hz, Ideal=466.60 Hz, Error=-0.19%) + DEFW #00E0 ; Octave 5, Note 71 - B (494.82 Hz, Ideal=493.88 Hz, Error=+0.19%) + + DEFW #00D4 ; Octave 6, Note 72 - C (522.83 Hz, Ideal=523.26 Hz, Error=-0.08%) C5 + DEFW #00C8 ; Octave 6, Note 73 - C# (554.20 Hz, Ideal=554.40 Hz, Error=-0.04%) + DEFW #00BD ; Octave 6, Note 74 - D (586.46 Hz, Ideal=587.32 Hz, Error=-0.15%) + DEFW #00B2 ; Octave 6, Note 75 - D# (622.70 Hz, Ideal=622.20 Hz, Error=+0.08%) + DEFW #00A8 ; Octave 6, Note 76 - E (659.77 Hz, Ideal=659.26 Hz, Error=+0.08%) + DEFW #009F ; Octave 6, Note 77 - F (697.11 Hz, Ideal=698.46 Hz, Error=-0.19%) + DEFW #0096 ; Octave 6, Note 78 - F# (738.94 Hz, Ideal=740.00 Hz, Error=-0.14%) + DEFW #008D ; Octave 6, Note 79 - G (786.10 Hz, Ideal=784.00 Hz, Error=+0.27%) + DEFW #0085 ; Octave 6, Note 80 - G# (833.39 Hz, Ideal=830.60 Hz, Error=+0.34%) + DEFW #007E ; Octave 6, Note 81 - A (879.69 Hz, Ideal=880.00 Hz, Error=-0.04%) + DEFW #0077 ; Octave 6, Note 82 - A# (931.43 Hz, Ideal=933.20 Hz, Error=-0.19%) + DEFW #0070 ; Octave 6, Note 83 - B (989.65 Hz, Ideal=987.76 Hz, Error=+0.19%) + + DEFW #006A ; Octave 7, Note 84 - C (1045.67 Hz, Ideal=1046.52 Hz, Error=-0.08%) C6 + DEFW #0064 ; Octave 7, Note 85 - C# (1108.41 Hz, Ideal=1108.80 Hz, Error=-0.04%) + DEFW #005E ; Octave 7, Note 86 - D (1179.16 Hz, Ideal=1174.64 Hz, Error=+0.38%) + DEFW #0059 ; Octave 7, Note 87 - D# (1245.40 Hz, Ideal=1244.40 Hz, Error=+0.08%) + DEFW #0054 ; Octave 7, Note 88 - E (1319.53 Hz, Ideal=1318.52 Hz, Error=+0.08%) + DEFW #004F ; Octave 7, Note 89 - F (1403.05 Hz, Ideal=1396.92 Hz, Error=+0.44%) + DEFW #004B ; Octave 7, Note 90 - F# (1477.88 Hz, Ideal=1480.00 Hz, Error=-0.14%) + DEFW #0047 ; Octave 7, Note 91 - G (1561.14 Hz, Ideal=1568.00 Hz, Error=-0.44%) + DEFW #0043 ; Octave 7, Note 92 - G# (1654.34 Hz, Ideal=1661.20 Hz, Error=-0.41%) + DEFW #003F ; Octave 7, Note 93 - A (1759.38 Hz, Ideal=1760.00 Hz, Error=-0.04%) + DEFW #003B ; Octave 7, Note 94 - A# (1878.65 Hz, Ideal=1866.40 Hz, Error=+0.66%) + DEFW #0038 ; Octave 7, Note 95 - B (1979.30 Hz, Ideal=1975.52 Hz, Error=+0.19%) + + DEFW #0035 ; Octave 8, Note 96 - C (2091.33 Hz, Ideal=2093.04 Hz, Error=-0.08%) C7 + DEFW #0032 ; Octave 8, Note 97 - C# (2216.81 Hz, Ideal=2217.60 Hz, Error=-0.04%) + DEFW #002F ; Octave 8, Note 98 - D (2358.31 Hz, Ideal=2349.28 Hz, Error=+0.38%) + DEFW #002D ; Octave 8, Note 99 - D# (2463.13 Hz, Ideal=2488.80 Hz, Error=-1.03%) + DEFW #002A ; Octave 8, Note 100 - E (2639.06 Hz, Ideal=2637.04 Hz, Error=+0.08%) + DEFW #0028 ; Octave 8, Note 101 - F (2771.02 Hz, Ideal=2793.84 Hz, Error=-0.82%) + DEFW #0025 ; Octave 8, Note 102 - F# (2995.69 Hz, Ideal=2960.00 Hz, Error=+1.21%) + DEFW #0023 ; Octave 8, Note 103 - G (3166.88 Hz, Ideal=3136.00 Hz, Error=+0.98%) + DEFW #0021 ; Octave 8, Note 104 - G# (3358.81 Hz, Ideal=3322.40 Hz, Error=+1.10%) + DEFW #001F ; Octave 8, Note 105 - A (3575.50 Hz, Ideal=3520.00 Hz, Error=+1.58%) + DEFW #001E ; Octave 8, Note 106 - A# (3694.69 Hz, Ideal=3732.80 Hz, Error=-1.02%) + DEFW #001C ; Octave 8, Note 107 - B (3958.59 Hz, Ideal=3951.04 Hz, Error=+0.19%) + + DEFW #001A ; Octave 9, Note 108 - C (4263.10 Hz, Ideal=4186.08 Hz, Error=+1.84%) C8 + DEFW #0019 ; Octave 9, Note 109 - C# (4433.63 Hz, Ideal=4435.20 Hz, Error=-0.04%) + DEFW #0018 ; Octave 9, Note 110 - D (4618.36 Hz, Ideal=4698.56 Hz, Error=-1.71%) + DEFW #0016 ; Octave 9, Note 111 - D# (5038.21 Hz, Ideal=4977.60 Hz, Error=+1.22%) + DEFW #0015 ; Octave 9, Note 112 - E (5278.13 Hz, Ideal=5274.08 Hz, Error=+0.08%) + DEFW #0014 ; Octave 9, Note 113 - F (5542.03 Hz, Ideal=5587.68 Hz, Error=-0.82%) + DEFW #0013 ; Octave 9, Note 114 - F# (5833.72 Hz, Ideal=5920.00 Hz, Error=-1.46%) + DEFW #0012 ; Octave 9, Note 115 - G (6157.81 Hz, Ideal=6272.00 Hz, Error=-1.82%) + DEFW #0011 ; Octave 9, Note 116 - G# (6520.04 Hz, Ideal=6644.80 Hz, Error=-1.88%) + DEFW #0010 ; Octave 9, Note 117 - A (6927.54 Hz, Ideal=7040.00 Hz, Error=-1.60%) + DEFW #000F ; Octave 9, Note 118 - A# (7389.38 Hz, Ideal=7465.60 Hz, Error=-1.02%) + DEFW #000E ; Octave 9, Note 119 - B (7917.19 Hz, Ideal=7902.08 Hz, Error=+0.19%) + + DEFW #000D ; Octave 10, Note 120 - C ( 8526.20 Hz, Ideal= 8372.16 Hz, Error=+1.84%) C9 + DEFW #000C ; Octave 10, Note 121 - C# ( 9236.72 Hz, Ideal= 8870.40 Hz, Error=+4.13%) + DEFW #000C ; Octave 10, Note 122 - D ( 9236.72 Hz, Ideal= 9397.12 Hz, Error=-1.71%) + DEFW #000B ; Octave 10, Note 123 - D# (10076.42 Hz, Ideal= 9955.20 Hz, Error=+1.22%) + DEFW #000B ; Octave 10, Note 124 - E (10076.42 Hz, Ideal=10548.16 Hz, Error=-4.47%) + DEFW #000A ; Octave 10, Note 125 - F (11084.06 Hz, Ideal=11175.36 Hz, Error=-0.82%) + DEFW #0009 ; Octave 10, Note 126 - F# (12315.63 Hz, Ideal=11840.00 Hz, Error=+4.02%) + DEFW #0009 ; Octave 10, Note 127 - G (12315.63 Hz, Ideal=12544.00 Hz, Error=-1.82%) + DEFW #0008 ; Octave 10, Note 128 - G# (13855.08 Hz, Ideal=13289.60 Hz, Error=+4.26%) + +; ------------------------- +; Play Note on MIDI Channel +; ------------------------- +; This routine turns on a note on the MIDI channel and sets its volume, if MIDI channel is assigned to the current string. +; Three bytes are sent, and have the following meaning: +; Byte 1: Channel number #00..#0F, with bits 4 and 7 set. +; Byte 2: Note number #00..#7F. +; Byte 3: Note velocity #00..#78. +; Entry: IX=Address of the channel data block. + +L116E: LD A,(IX+#01) ; Is a MIDI channel assigned to this string? + OR A ; + RET M ; Return if not. + +;A holds the assigned channel number (#00..#0F) + + OR #90 ; Set bits 4 and 7 of the channel number. A=#90..#9F. + CALL L11A3 ; Write byte to MIDI device. + + LD A,(IX+#00) ; The note number. + CALL L11A3 ; Write byte to MIDI device. + + LD A,(IX+#04) ; Fetch the channel's volume. + RES 4,A ; Ensure the 'using envelope' bit is reset so + SLA A ; that A holds a value between #00 and #0F. + SLA A ; Multiply by 8 to increase the range to #00..#78. + SLA A ; A=Note velocity. + CALL L11A3 ; Write byte to MIDI device. + RET ; [Could have saved 1 byte by using JP #11A3 (ROM 0)] + +; --------------------- +; Turn MIDI Channel Off +; --------------------- +; This routine turns off a note on the MIDI channel, if a MIDI channel is assigned to the current string. +; Three bytes are sent, and have the following meaning: +; Byte 1: Channel number #00..#0F, with bit 7 set. +; Byte 2: Note number #00..#7F. +; Byte 3: Note velocity #40. +; Entry: IX=Address of the channel data block. + +L118D: LD A,(IX+#01) ; Is a MIDI channel assigned to this string? + OR A ; + RET M ; Return if not. + +;A holds the assigned channel number (#00..#0F) + + OR #80 ; Set bit 7 of the channel number. A=#80..#8F. + CALL L11A3 ; Write byte to MIDI device. + + LD A,(IX+#00) ; The note number. + CALL L11A3 ; Write byte to MIDI device. + + LD A,#40 ; The note velocity. + CALL L11A3 ; Write byte to MIDI device. + RET ; [Could have saved 1 byte by using JP #11A3 (ROM 0)] + +; ------------------------ +; Send Byte to MIDI Device +; ------------------------ +; This routine sends a byte to the MIDI port. MIDI devices communicate at 31250 baud, +; although this routine actually generates a baud rate of 31388, which is within the 1% +; tolerance supported by MIDI devices. +; Entry: A=Byte to send. + +L11A3: LD L,A ; Store the byte to send. + + LD BC,#FFFD ; + LD A,#0E ; + OUT (C),A ; Select register 14 - I/O port. + + LD BC,#BFFD ; + LD A,#FA ; Set RS232 'RXD' transmit line to 0. (Keep KEYPAD 'CTS' output line low to prevent the keypad resetting) + OUT (C),A ; Send out the START bit. + + LD E,#03 ; (7) Introduce delays such that the next bit is output 113 T-states from now. + +L11B4: DEC E ; (4) + JR NZ,L11B4 ; (12/7) + + NOP ; (4) + NOP ; (4) + NOP ; (4) + NOP ; (4) + + LD A,L ; (4) Retrieve the byte to send. + + LD D,#08 ; (7) There are 8 bits to send. + +L11BE: RRA ; (4) Rotate the next bit to send into the carry. + LD L,A ; (4) Store the remaining bits. + JP NC,L11C9 ; (10) Jump if it is a 0 bit. + + LD A,#FE ; (7) Set RS232 'RXD' transmit line to 1. (Keep KEYPAD 'CTS' output line low to prevent the keypad resetting) + OUT (C),A ; (11) + JR L11CF ; (12) Jump forward to process the next bit. + +L11C9: LD A,#FA ; (7) Set RS232 'RXD' transmit line to 0. (Keep KEYPAD 'CTS' output line low to prevent the keypad resetting) + OUT (C),A ; (11) + JR L11CF ; (12) Jump forward to process the next bit. + +L11CF: LD E,#02 ; (7) Introduce delays such that the next data bit is output 113 T-states from now. + +L11D1: DEC E ; (4) + JR NZ,L11D1 ; (12/7) + + NOP ; (4) + ADD A,#00 ; (7) + + LD A,L ; (4) Retrieve the remaining bits to send. + DEC D ; (4) Decrement the bit counter. + JR NZ,L11BE ; (12/7) Jump back if there are further bits to send. + + NOP ; (4) Introduce delays such that the stop bit is output 113 T-states from now. + NOP ; (4) + ADD A,#00 ; (7) + NOP ; (4) + NOP ; (4) + + LD A,#FE ; (7) Set RS232 'RXD' transmit line to 0. (Keep KEYPAD 'CTS' output line low to prevent the keypad resetting) + OUT (C),A ; (11) Send out the STOP bit. + + LD E,#06 ; (7) Delay for 101 T-states (28.5us). + +L11E7: DEC E ; (4) + JR NZ,L11E7 ; (12/7) + + RET ; (10) + + +; ============================================= +; CASSETTE / RAM DISK COMMAND ROUTINES - PART 1 +; ============================================= + +; ------------ +; SAVE Routine +; ------------ + +L11EB: LD HL,FLAGS3 ; #5B66. + SET 5,(HL) ; Indicate SAVE. + JR L1205 + +; ------------ +; LOAD Routine +; ------------ + +L11F2: LD HL,FLAGS3 ; #5B66. + SET 4,(HL) ; Indicate LOAD. + JR L1205 + +; -------------- +; VERIFY Routine +; -------------- + +L11F9: LD HL,FLAGS3 ; #5B66. + SET 7,(HL) ; Indicate VERIFY. + JR L1205 + +; ------------- +; MERGE Routine +; ------------- + +L1200: LD HL,FLAGS3 ; #5B66. + SET 6,(HL) ; Indicate MERGE. + +L1205: LD HL,FLAGS3 ; #5B66. + RES 3,(HL) ; Indicate using cassette. + RST #18 ; Get current character. + CP '!' ; #21. '!' + JP NZ,L13BE ; Jump ahead to handle cassette command. + +;RAM disk operation + + LD HL,FLAGS3 ; #5B66. + SET 3,(HL) ; Indicate using RAM disk. + RST #20 ; Move on to next character. + JP L13BE ; Jump ahead to handle RAM disk command. + +L1219: CALL L05AC ; Produce error report. + DEFB #0B ; "C Nonsense in BASIC" + +; ------------------------- +; RAM Disk Command Handling +; ------------------------- +; The information relating to the file is copied into memory in #5B66 (FLAGS3) +; to ensure that it is available once other RAM banks are switched in. +; This code is very similar to that in the ZX Interface 1 ROM at #08F6. +; Entry: HL=Start address. +; IX=File header descriptor. + +L121D: LD (HD_0D),HL ; #5B74. Save start address. + + LD A,(IX+#00) ; Transfer header file information + LD (HD_00),A ; #5B71. from IX to HD_00 onwards. + LD L,(IX+#0B) ; + LD H,(IX+#0C) ; + LD (HD_0B),HL ; #5B72. + LD L,(IX+#0D) ; + LD H,(IX+#0E) ; + LD (HD_11),HL ; #5B78. + LD L,(IX+#0F) ; + LD H,(IX+#10) ; + LD (HD_0F),HL ; #5B76. + +;A copy of the header information has now been copied from IX+#00 onwards to HD_00 onwards + + OR A ; Test file type. + JR Z,L124E ; Jump ahead for a program file. + + CP #03 ; + JR Z,L124E ; Jump ahead for a CODE/SCREEN$ file. + +;An array type + + LD A,(IX+#0E) ; + LD (HD_0F),A ; #5B76. Store array name. + +L124E: PUSH IX ; IX points to file header. + POP HL ; Retrieve into HL. + + INC HL ; HL points to filename. + LD DE,N_STR1 ; #5B67. + LD BC,#000A ; + LDIR ; Copy the filename. + + LD HL,FLAGS3 ; #5B66. + BIT 5,(HL) ; SAVE operation? + JP NZ,L1BAD ; Jump ahead if SAVE. + +; Load / Verify or Merge +; ---------------------- + + LD HL,HD_00 ; #5B71. + LD DE,SC_00 ; #5B7A. + LD BC,#0007 ; + LDIR ; Transfer requested details from HD_00 onwards into SC_00 onwards. + + CALL L1C2E ; Find and load requested file header into HD_00 (#5B71). + +;The file exists else the call above would have produced an error "h file does not exist" + + LD A,(SC_00) ; #5B7A. Requested file type. + LD B,A ; + LD A,(HD_00) ; #5B71. Loaded file type. + CP B ; + JR NZ,L1280 ; Error 'b' if file types do not match. + + CP #03 ; Is it a CODE file type? + JR Z,L1290 ; Jump ahead to avoid MERGE program/array check. + + JR C,L1284 ; Only file types 0, 1 and 2 are OK. + +L1280: CALL L05AC ; Produce error report. + DEFB #1D ; "b Wrong file type" + +L1284: LD A,(FLAGS3) ; #5B66. + BIT 6,A ; Is it a MERGE program/array operation? + JR NZ,L12C5 ; Jump ahead if so. + + BIT 7,A ; Is it a VERIFY program/array operation? + JP Z,L12DB ; Jump ahead if LOAD. + +;Either a verify program/array or a load/verify CODE/SCREEN$ type file + +L1290: LD A,(FLAGS3) ; #5B66. + BIT 6,A ; MERGE operation? + JR Z,L129B ; Jump ahead if VERIFY. + +;Cannot merge CODE/SCREEN$ + + CALL L05AC ; Produce error report. + DEFB #1C ; "a MERGE error" + +; ------------------------ +; RAM Disk VERIFY! Routine +; ------------------------ + +L129B: LD HL,(SC_0B) ; #5B7B. Length requested. + LD DE,(HD_0B) ; #5B72. File length. + LD A,H ; + OR L ; + JR Z,L12AE ; Jump ahead if requested length is 0, i.e. not specified. + + SBC HL,DE ; Is file length <= requested length? + JR NC,L12AE ; Jump ahead if so; requested length is OK. + +;File was smaller than requested + + CALL L05AC ; Produce error report. + DEFB #1E ; "c CODE error" + +L12AE: LD HL,(SC_0D) ; #5B7D. Fetch start address. + LD A,H ; + OR L ; Is length 0, i.e. not provided? + JR NZ,L12B8 ; Jump ahead if start address was provided. + + LD HL,(HD_0D) ; #5B74. Not provided so use file's start address. + +L12B8: LD A,(HD_00) ; #5B71. File type. + AND A ; Is it a program? + JR NZ,L12C1 ; Jump ahead if not. + + LD HL,(#5C53) ; PROG. Set start address as start of program area. + +L12C1: CALL L137E ; Load DE bytes at address pointed to by HL. + ; [The Spectrum 128 manual states that the VERIFY keyword is not used with the RAM disk yet it clearly is, + ; although verifying a RAM disk file simply loads it in just as LOAD would do. To support verifying, the routine + ; at #1E37 (ROM 0) which loads blocks of data would need to be able to load or verify a block. The success status would + ; then need to be propagated back to here via routines at #137E (ROM 0), #1C4B (ROM 0) and #1E37 (ROM 0)] + RET ; [Could have saved 1 byte by using JP #137E (ROM 0), although could have saved a lot more by not supporting the + ; VERIFY keyword at all] + +; ----------------------- +; RAM Disk MERGE! Routine +; ----------------------- + +L12C5: LD BC,(HD_0B) ; #5B72. File length. + PUSH BC ; Save the length. + INC BC ; Increment for terminator #80 (added later). + RST #28 ; + DEFW BC_SPACES ; #0030. Create room in the workspace for the file. + LD (HL),#80 ; Insert terminator. + + EX DE,HL ; HL=Start address. + POP DE ; DE=File length. + PUSH HL ; Save start address. + CALL L137E ; Load DE bytes to address pointed to by HL. + POP HL ; Retrieve start address. + + RST #28 ; + DEFW ME_CONTRL+#18; #08CE. Delegate actual merge handling to ROM 1. + RET ; + +; ---------------------- +; RAM Disk LOAD! Routine +; ---------------------- + +L12DB: LD DE,(HD_0B) ; #5B72. File length. + LD HL,(SC_0D) ; #5B7D. Requested start address. + PUSH HL ; Save requested start address. + LD A,H ; + OR L ; Was start address specified? (0 if not). + JR NZ,L12ED ; Jump ahead if start address specified. + +;Start address was not specified + + INC DE ; Allow for variable overhead. + INC DE ; + INC DE ; + EX DE,HL ; HL=File Length+3. + JR L12F6 ; Jump ahead to test if there is room. + +;A start address was specified + +L12ED: LD HL,(SC_0B) ; #5B7B. Requested length. + EX DE,HL ; DE=Requested length. HL=File length. + SCF ; + SBC HL,DE ; File length-Requested Length-1 + JR C,L12FF ; Jump if file is smaller than requested. + +;Test if there is room since file is bigger than requested + +L12F6: LD DE,#0005 ; + ADD HL,DE ; + LD B,H ; + LD C,L ; Space required in BC. + RST #28 ; + DEFW TEST_ROOM ; #1F05. Will automatically produce error '4' if out of memory. + +;Test file type + +L12FF: POP HL ; Requested start address. + LD A,(HD_00) ; #5B71. Get requested file type. + +L1303: AND A ; Test file type. + JR Z,L1335 ; Jump if program file type. + +; Array type +; ---------- + + LD A,H ; + OR L ; Was start address of existing array specified? + JR Z,L1315 ; Jump ahead if not. + +;Start address of existing array was specified + + DEC HL ; + LD B,(HL) ; + DEC HL ; + LD C,(HL) ; Fetch array length. + DEC HL ; + INC BC ; + INC BC ; + INC BC ; Allow for variable header. + RST #28 ; + DEFW RECLAIM_2 ; #19E8. Delete old array. + +;Insert new array entry into variables area + +L1315: LD HL,(#5C59) ; E_LINE. + DEC HL ; Point to end + LD BC,(HD_0B) ; #5B72. Array length. + PUSH BC ; Save array length. + INC BC ; Allow for variable header. + INC BC ; + INC BC ; + LD A,(SC_0F) ; #5B7F. Get array name. + PUSH AF ; Save array name. + RST #28 ; + DEFW MAKE_ROOM ; #1655. Create room for new array. + INC HL + POP AF ; + LD (HL),A ; Store array name. + POP DE ; + INC HL ; + LD (HL),E ; + INC HL ; + LD (HL),D ; Store array length. + INC HL ; + +L1331: CALL L137E ; Load DE bytes to address pointed to by HL. + RET ; [Could have saved 1 byte by using JP #137E (ROM 0)] + +; Program type +; ------------ + +L1335: LD HL,FLAGS3 ; #5B66. + RES 1,(HL) ; Signal do not auto-run BASIC program. + + LD DE,(#5C53) ; PROG. Address of start of BASIC program. + LD HL,(#5C59) ; E_LINE. Address of end of program area. + DEC HL ; Point before terminator. + RST #28 ; + DEFW RECLAIM ; #19E5. Delete current BASIC program. + + LD BC,(HD_0B) ; #5B72. Fetch file length. + LD HL,(#5C53) ; PROG. Address of start of BASIC program. + RST #28 ; + DEFW MAKE_ROOM ; #1655. Create room for the file. + INC HL ; Allow for terminator. + + LD BC,(HD_0F) ; #5B76. Length of variables. + ADD HL,BC ; Determine new address of variables. + LD (#5C4B),HL ; VARS. + + LD A,(HD_11+1) ; #5B79. Fetch high byte of auto-run line number. + LD H,A ; + AND #C0 ; + JR NZ,L1370 ; If holds #80 then no auto-run line number specified. + + LD A,(HD_11) ; #5B78. Low byte of auto-run line number. + LD L,A ; + LD (#5C42),HL ; NEWPPC. Set line number to run. + LD (IY+#0A),#00 ; NSPPC. Statement 0. + + LD HL,FLAGS3 ; #5B66. + SET 1,(HL) ; Signal auto-run BASIC program. + +L1370: LD HL,(#5C53) ; PROG. Address of start of BASIC program. + LD DE,(HD_0B) ; #5B72. Program length. + DEC HL ; + LD (#5C57),HL ; NXTLIN. Set the address of next line to the end of the program. + INC HL ; + JR L1331 ; Jump back to load program bytes. + +; ------------------- +; RAM Disk Load Bytes +; ------------------- +; Make a check that the requested length is not zero before proceeding to perform +; the LOAD, MERGE or VERIFY. Note that VERIFY simply performs a LOAD. +; Entry: HL=Destination address. +; DE=Length. +; IX=Address of catalogue entry. +; HD_00-HD_11 holds file header information. + +L137E: LD A,D ; + OR E ; + RET Z ; Return if length is zero. + + CALL L1C4B ; Load bytes + RET ; [Could have used JP #1C4B (ROM 0) to save 1 byte] + +; ------------------------------ +; Get Expression from BASIC Line +; ------------------------------ +; Returns in BC. + +L1385: RST #28 ; Expect an expression on the BASIC line. + DEFW EXPT_EXP ; #1C8C. + BIT 7,(IY+#01) ; Return early if syntax checking. + RET Z ; + + PUSH AF ; Get the item off the calculator stack + RST #28 ; + DEFW STK_FETCH ; #2BF1. + POP AF ; + RET ; + +; ----------------------- +; Check Filename and Copy +; ----------------------- +; Called to check a filename for validity and to copy it into N_STR1 (#5B67). + +L1393: RST #20 ; Advance the pointer into the BASIC line. + CALL L1385 ; Get expression from BASIC line. + RET Z ; Return if syntax checking. + + PUSH AF ; [No need to save AF - see comment below] + + LD A,C ; Check for zero length. + OR B ; + JR Z,L13BA ; Jump if so to produce error report "f Invalid name". + + LD HL,#000A ; Check for length greater than 10. + SBC HL,BC ; + JR C,L13BA ; Jump if so to produce error report "f Invalid name". + + PUSH DE ; Save the filename start address. + PUSH BC ; Save the filename length. + + LD HL,N_STR1 ; #5B67. HL points to filename buffer. + LD B,#0A ; + LD A,#20 ; + +L13AD: LD (HL),A ; Fill it with 10 spaces. + INC HL ; + DJNZ L13AD ; + + POP BC ; Restore filename length. + POP HL ; Restore filename start address. + + LD DE,N_STR1 ; #5B67. DE points to where to store the filename. + LDIR ; Perform the copy. + + POP AF ; [No need to have saved AF as not subsequently used] + RET ; + +L13BA: CALL L05AC ; Produce error report. + DEFB #21 ; "f Invalid name" + +; ------------------------------------ +; Cassette / RAM Disk Command Handling +; ------------------------------------ +; Handle SAVE, LOAD, MERGE, VERIFY commands. +; Bit 3 of FLAGS3 indicates whether a cassette or RAM disk command. +; This code is very similar to that in ROM 1 at #0605. + +L13BE: RST 28H + DEFW EXPT_EXP ; #1C8C. Pass the parameters of the 'name' to the calculator stack. + + BIT 7,(IY+#01) ; + JR Z,L1407 ; Jump ahead if checking syntax. + + LD BC,#0011 ; Size of save header, 17 bytes. + LD A,(#5C74) ; T_ADDR. Indicates which BASIC command. + AND A ; Is it SAVE? + JR Z,L13D2 ; Jump ahead if so. + + LD C,#22 ; Otherwise need 34d bytes for LOAD, MERGE and VERIFY commands. + ; 17 bytes for the header of the requested file, and 17 bytes for the files tested from tape. + +L13D2: RST #28 ; + DEFW BC_SPACES ; #0030. Create space in workspace. + + PUSH DE ; Get start of the created space into IX. + POP IX ; + + LD B,#0B ; Clear the filename. + LD A,#20 ; + +L13DC: LD (DE),A ; Set all characters to spaces. + INC DE ; + DJNZ L13DC ; + + LD (IX+#01),#FF ; Indicate a null name. + RST #28 ; The parameters of the name are fetched. + DEFW STK_FETCH ; #2BF1. + + LD HL,#FFF6 ; = -10. + DEC BC ; + ADD HL,BC ; + INC BC ; + JR NC,L1400 ; Jump ahead if filename length within 10 characters. + + LD A,(#5C74) ; T_ADDR. Indicates which BASIC command. + AND A ; Is it SAVE? + JR NZ,L13F9 ; Jump ahead if not since LOAD, MERGE and VERIFY can have null filenames. + + CALL L05AC ; Produce error report. + DEFB #0E ; "F Invalid file name" + +;Continue to handle the name of the program. + +L13F9: LD A,B + OR C ; + JR Z,L1407 ; Jump forward if the name has a null length. + + LD BC,#000A ; Truncate longer filenames. + +;The name is now transferred to the work space (second location onwards) + +L1400: PUSH IX ; + POP HL ; Transfer address of the workspace to HL. + INC HL ; Step to the second location. + EX DE,HL ; + LDIR ; Copy the filename. + +;The many different parameters, if any, that follow the command are now considered. +;Start by handling 'xxx "name" DATA'. + +L1407: RST #18 ; Get character from BASIC line. + CP #E4 ; Is it 'DATA'? + JR NZ,L145F ; Jump if not DATA. + +; 'xxx "name" DATA' +; ----------------- + + LD A,(#5C74) ; T_ADDR. Check the BASIC command. + CP #03 ; Is it MERGE? + JP Z,L1219 ; "C Nonsense in BASIC" if so. + + RST #20 ; Get next character from BASIC line. + RST #28 ; + DEFW LOOK_VARS ; #28B2. Look in the variables area for the array. + JR NC,L142F ; Jump if handling an existing array. + + LD HL,#0000 ; Signal 'using a new array'. + BIT 6,(IY+#01) ; FLAGS. Is it a string Variable? + JR Z,L1425 ; Jump forward if so. + + SET 7,C ; Set bit 7 of the array's name. + +L1425: LD A,(#5C74) ; T_ADDR. + DEC A ; Give an error if trying to + JR Z,L1444 ; SAVE or VERIFY a new array. + + CALL L05AC ; Produce error report. + DEFB #01 ; "2 Variable not found" + +;Continue with the handling of an existing array + +L142F: JP NZ,L1219 ; Jump if not an array to produce "C Nonsense in BASIC". + + BIT 7,(IY+#01) ; FLAGS. + JR Z,L1451 ; Jump forward if checking syntax. + + LD C,(HL) ; + INC HL ; Point to the 'low length' of the variable. + LD A,(HL) ; The low length byte goes into + LD (IX+#0B),A ; the work space. + INC HL ; + LD A,(HL) ; The high length byte goes into + LD (IX+#0C),A ; the work space. + INC HL ; Step past the length bytes. + +;The next part is common to both 'old' and 'new' arrays + +L1444: LD (IX+#0E),C ; Copy the array's name. + LD A,#01 ; Assume an array of numbers - Code #01. + BIT 6,C ; + JR Z,L144E ; Jump if it is so. + + INC A ; Indicate it is an array of characters - Code #02. + +L144E: LD (IX+#00),A ; Save the 'type' in the first location of the header area. + +;The last part of the statement is examined before joining the other pathways + +L1451: EX DE,HL ; Save the pointer in DE. + RST #20 ; + CP ')' ; #29. Is the next character a ')'? + JR NZ,L142F ; Give report C if it is not. + + RST #20 ; Advance to next character. + CALL L18A1 ; Move on to the next statement if checking syntax. + EX DE,HL ; Return the pointer to the HL. + ; (The pointer indicates the start of an existing array's contents). + JP L1519 ; Jump forward. + +; Now Consider 'SCREEN$' + +L145F: CP #AA ; Is the present code the token 'SCREEN$'? + JR NZ,L1482 ; Jump ahead if not. + +; 'xxx "name" SCREEN$' +; -------------------- + + LD A,(#5C74) ; T_ADDR_lo. Check the BASIC command. + CP #03 ; Is it MERGE? + JP Z,L1219 ; Jump to "C Nonsense in BASIC" if so since it is not possible to have 'MERGE name SCREEN$'. + + RST #20 ; Advance pointer into BASIC line. + CALL L18A1 ; Move on to the next statement if checking syntax. + + LD (IX+#0B),#00 ; Length of the block. + LD (IX+#0C),#1B ; The display area and the attribute area occupy #1800 locations. + + LD HL,#4000 ; Start of the block, beginning of the display file #4000. + LD (IX+#0D),L ; + LD (IX+#0E),H ; Store in the workspace. + JR L14CF ; Jump forward. + +; Now consider 'CODE' + +L1482: CP #AF ; Is the present code the token 'CODE'? + JR NZ,L14D5 ; Jump ahead if not. + +; 'xxx "name" CODE' +; ----------------- + + LD A,(#5C74) ; T_ADDR_lo. Check the BASIC command. + CP #03 ; Is it MERGE? + JP Z,L1219 ; Jump to "C Nonsense in BASIC" if so since it is not possible to have 'MERGE name CODE'. + + RST #20 ; Advance pointer into BASIC line. + RST #28 ; + DEFW PR_ST_END ; #2048. + JR NZ,L14A0 ; Jump forward if the statement has not finished + + LD A,(#5C74) ; T_ADDR_lo. + AND A ; It is not possible to have 'SAVE name CODE' by itself. + JP Z,L1219 ; Jump if so to produce "C Nonsense in BASIC". + + RST #28 ; + DEFW USE_ZERO ; #1CE6. Put a zero on the calculator stack - for the 'start'. + JR L14AF ; Jump forward. + +;Look for a 'starting address' + +L14A0: RST #28 ; + DEFW EXPT_1NUM ; #1C82. Fetch the first number. + RST #18 ; + CP ',' ; #2C. Is the present character a ','? + JR Z,L14B4 ; Jump if it is - the number was a 'starting address' + + LD A,(#5C74) ; T_ADDR_lo. + AND A ; Refuse 'SAVE name CODE' that does not have a 'start' and a 'length'. + JP Z,L1219 ; Jump if so to produce "C Nonsense in BASIC". + +L14AF: RST #28 ; + DEFW USE_ZERO ; #1CE6. Put a zero on the calculator stack - for the 'length'. + JR L14B8 ; Jump forward. + +;Fetch the 'length' as it was specified + +L14B4: RST #20 ; Advance to next character. + RST #28 ; + DEFW EXPT_1NUM ; #1C82. Fetch the 'length'. + +;The parameters are now stored in the header area of the work space + +L14B8: CALL L18A1 ; But move on to the next statement now if checking syntax. + RST #28 ; + DEFW FIND_INT2 ; #1E99. Compress the 'length' into BC. + LD (IX+#0B),C ; Store the length of the CODE block. + LD (IX+#0C),B ; + RST #28 ; + DEFW FIND_INT2 ; #1E99. Compress the 'starting address' into BC. + LD (IX+#0D),C ; Store the start address of the CODE block. + LD (IX+#0E),B ; + LD H,B ; Transfer start address pointer to HL. + LD L,C ; + +;'SCREEN$' and 'CODE' are both of type 3 + +L14CF: LD (IX+#00),#03 ; Store file type = #03 (CODE). + JR L1519 ; Rejoin the other pathways. + +; 'xxx "name"' / 'SAVE "name" LINE' +; --------------------------------- + +;Now consider 'LINE' and 'no further parameters' + +L14D5: CP #CA ; Is the present code the token 'LINE'? + JR Z,L14E2 ; Jump ahead if so. + + CALL L18A1 ; Move on to the next statement if checking syntax. + LD (IX+#0E),#80 ; Indicate no LINE number. + JR L14F9 ; Jump forward. + +;Fetch the 'line number' that must follow 'LINE' + +L14E2: LD A,(#5C74) ; T_ADDR_lo. Only allow 'SAVE name LINE number'. + AND A ; Is it SAVE? + JP NZ,L1219 ; Produce "C Nonsense in BASIC" if not. + + RST #20 ; Advance pointer into BASIC line. + RST #28 ; Get LINE number onto calculator stack + DEFW EXPT_1NUM ; #1C82. Pass the number to the calculator stack. + CALL L18A1 ; Move on to the next statement if checking syntax. + RST #28 ; Retrieve LINE number from calculator stack + DEFW FIND_INT2 ; #1E99. Compress the 'line number' into BC. + LD (IX+#0D),C ; Store the LINE number. + LD (IX+#0E),B ; + +;'LINE' and 'no further parameters' are both of type 0 + +L14F9: LD (IX+#00),#00 ; Store file type = #00 (program). + LD HL,(#5C59) ; E_LINE. The pointer to the end of the variables area. + LD DE,(#5C53) ; PROG. The pointer to the start of the BASIC program. + SCF ; + SBC HL,DE ; Perform the subtraction to find the length of the 'program + variables'. + LD (IX+#0B),L ; + LD (IX+#0C),H ; Store the length. + + LD HL,(#5C4B) ; VARS. Repeat the operation but this + SBC HL,DE ; time storing the length of the + LD (IX+#0F),L ; 'program' only. + LD (IX+#10),H ; + EX DE,HL ; Transfer pointer to HL. + +;In all cases the header information has now been prepared: +;- The location 'IX+00' holds the type number. +;- Locations 'IX+01 to IX+0A' holds the name (#FF in 'IX+01' if null). +;- Locations 'IX+0B & IX+0C' hold the number of bytes that are to be found in the 'data block'. +;- Locations 'IX+0D to IX+10' hold a variety of parameters whose exact interpretation depends on the 'type'. + +;The routine continues with the first task being to separate SAVE from LOAD, VERIFY and MERGE. + +L1519: LD A,(FLAGS3) ; #5B66. + BIT 3,A ; Using RAM disk? + JP NZ,L121D ; Jump if the operation is on the RAM disk. + + LD A,(#5C74) ; T_ADDR_lo. Get the BASIC command. + AND A ; Is it SAVE? + JR NZ,L152B ; Jump ahead if not. + + RST #28 ; + DEFW SA_CONTROL ; #0970. Run the save routine in ROM 1. + RET ; + +;In the case of a LOAD, VERIFY or MERGE command the first seventeen bytes of the 'header area' +;in the work space hold the prepared information, as detailed above; +;and it is now time to fetch a 'header' from the tape. + +L152B: RST #28 ; + DEFW SA_ALL+#0007 ; #0761. Run the load/merge/verify routine in ROM 1. + RET ; + + +; ======================== +; EDITOR ROUTINES - PART 1 +; ======================== + +; ---------------------------------------------- +; Relist the BASIC Program from the Current Line +; ---------------------------------------------- +; This routine lists the BASIC program from the current line number. It initially shows the last line displayed but rows may subsequently +; be scrolled up until the required BASIC line has been found. The structure of the ROM program only supports listing BASIC lines that are +; 20 rows or less; larger lines are shown truncated to 20 rows. + +L152F: LD HL,#EEF5 ; Flags. + RES 0,(HL) ; Signal this is not the current line. + SET 1,(HL) ; Signal not yet located the current line. + +;A loop is entered to display a screenful of program listing. If the current line number is not found in the lines displayed then all +;lines are scrolled up and the listing reproduced. This procedure repeats until the current line number has been found and displayed. + +L1536: LD HL,(#5C49) ; E_PPC. Fetch current line number. + LD A,H ; + OR L ; Is there a currently selected line? + JR NZ,L1540 ; Jump ahead if so. + + LD (#EC06),HL ; Set to #0000 to indicate no editable characters before the cursor. + +L1540: LD A,(#F9DB) ; Fetch the number of rows of the BASIC line that are in the Above-Screen Line Edit Buffer, + PUSH AF ; i.e. that are off the top of the screen. + + LD HL,(#FC9A) ; Line number of the BASIC line at the top of the screen (or 0 for the first line). + CALL L334A ; Find closest line number (or #0000 if no subsequent line exists). + LD (#F9D7),HL ; Store the line number of the BASIC line being edited in the buffer. + + CALL L3222 ; Set default Above-Screen Line Edit Buffer settings. + CALL L30D6 ; Set default Below-Screen Line Edit Buffer settings. + + POP AF ; A=Number of rows of the BASIC line that are in the Above-Screen Line Edit Buffer. + +L1554: OR A ; Are there any rows off the top of the screen? + JR Z,L1563 ; Jump ahead if not. + +;The current settings indicate that the top BASIC line straggles into the Above-Screen Line Edit Buffer. It is therefore necessary to insert the current +;BASIC line into the Below-Screen Line Edit Buffer and then shift the appropriate number of rows into the Above-Screen Line Edit Buffer. + + PUSH AF ; Save the number of rows off the top of the screen. + CALL L30DF ; Copy a BASIC line from the program area into the Below-Screen Line Edit Buffer. + EX DE,HL ; DE=Address of the Below-Screen Line Edit Buffer. + CALL L326A ; Shift up a row into the Above-Screen Line Edit Buffer. + POP AF ; Retrieve the number of rows off the top of the screen. + DEC A ; Decrement the number of rows. + JR L1554 ; Jump back to shift up another row if required. + +;Either there the top BASI Cline does not straggle off the top of the the screen or the appropriate number of rows have been copied into the +;Above-Screen Line Edit Buffer. In the latter case, the Below-Screen Line Edit Buffer contains the remaining rows of the BASIC line and which +;be copied into the top of the Screen Line Edit Buffer. + +L1563: LD C,#00 ; C=Row 0. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the first row, as specified in C. + + LD B,C ; B=Row 0. + LD A,(#EC15) ; The number of editing rows on screen. + LD C,A ; C=Number of editing rows on screen. + PUSH BC ; B=Row number, C=Number of editing rows on screen. + PUSH DE ; DE=Start address in Screen Line Edit Buffer of the first row. + +;Enter a loop to copy BASIC line rows into the Screen Line Edit Buffer. The Below-Screen Line Edit Buffer is used as a temporary store for holding each BASIC line +;as it is copied into the Screen Line Edit Buffer. If the top BASIC line straggles above the screen then this loop is entered with the remains of the line +;already in the Below-Screen Line Edit Buffer. + +L156F: CALL L30DF ; Shift up all rows of the BASIC line in the Below-Screen Line Edit Buffer, or if empty then copy a BASIC line from the program area into it. + ; If no BASIC line available then empty the first row of the Below-Screen Line Edit Buffer. + + LD A,(#EEF5) ; Listing flags. + BIT 1,A ; Has the current line been previously found? + JR Z,L1596 ; Jump if so. + +;The current line has not yet been found so examine the current row in case it is the current line + + PUSH DE ; DE=Start address in Screen Line Edit Buffer of the current row. + PUSH HL ; HL=Address of the first row in the Below-Screen Line Edit Buffer. + + LD DE,#0020 ; + ADD HL,DE ; Point to the flag byte for the first row. + BIT 0,(HL) ; Is it the first row of a BASIC line? + JR Z,L1594 ; Jump if not. + +;The Below-Screen Line Edit Buffer contains a complete BASIC line so determine whether this is the current line + + INC HL ; + LD D,(HL) ; Get line number into DE. + INC HL ; + LD E,(HL) ; + OR A ; + LD HL,(#5C49) ; E_PPC. Current line number. + SBC HL,DE ; + JR NZ,L1594 ; Jump ahead unless this is the current line. + + LD HL,#EEF5 ; + SET 0,(HL) ; Signal this is the current line. + +L1594: POP HL ; HL=Address of the current row in the Below-Screen Line Edit Buffer. + POP DE ; DE=Start address in Screen Line Edit Buffer of the current row. + +;Copy the row of the BASIC line from the Below-Screen Line Edit Buffer into the Screen Line Edit Buffer + +L1596: PUSH BC ; B=Row number, C=Number of editing rows on screen. + PUSH HL ; HL=Address of the current row in the Below-Screen Line Edit Buffer. + LD BC,#0023 ; + LDIR ; Copy the first row of the BASIC line in the Below-Screen Line Edit Buffer into the next row of the Screen Line Edit Buffer. + POP HL ; HL=Address of the current row in the Below-Screen Line Edit Buffer. + POP BC ; B=Row number, C=Number of editing rows on screen. + + PUSH DE ; DE=Start address in Screen Line Edit Buffer of the next row. + PUSH BC ; B=Row number, C=Number of editing rows on screen. + EX DE,HL ; DE=Address of the current row in the Below-Screen Line Edit Buffer. + + LD HL,#EEF5 ; Flags. + BIT 0,(HL) ; Is this the current line? + JR Z,L15D3 ; Jump if not. + +;This is the current line so scan across the BASIC line to locate the cursor column position + + LD B,#00 ; Column 0. + +L15AB: LD HL,(#EC06) ; HL=Count of the number of editable characters in the BASIC line up to the cursor within the Screen Line Edit Buffer. + LD A,H ; + OR L ; Are there any editable characters in this row prior to the cursor? + JR Z,L15C0 ; Jump if there are none, i.e. cursor at start of the row. + +;There are editable characters on this row prior to the cursor + +; [*BUG* - Entering ' 10 REM' or '0010 REM' will insert the line into the program area but instead of placing the cursor on the following row it is placed after the following +; BASIC line, or if the line inserted was the last in the program then the cursor is placed on row 20. The bug occurs due to the leading spaces or zeros, and hence will apply +; to every BASIC command. When the line is inserted into the Screen Line Edit Buffer, the leading spaces are discarded and hence the line length is shorter than that typed +; in. However, it is the typed in line length that is used when parsing the BASIC line in the Screen Line Edit Buffer and as a result this causes an +; attempt to find the remaining characters on the following row of the Screen Line Edit Buffer. If another BASIC line is on the following Screen Line Edit Buffer +; row then the search completes and the cursor is placed on the row after this BASIC line. If there is not a BASIC line on the following +; row then the search continues on the next row. Since this will also be empty, the search advances onto the next row, and then the next, and so +; on until row 20 is reached. To fix the bug, the typed in character count until the cursor (held in #EC06) ideally needs to be adjusted to match the actual number of characters stored +; in the Screen Line Edit Buffer. However, this is not a trivial change to implement. A simpler solution to fix the bug is to intercept when a move to the next row is made and +; to determine whether the BASIC line actually continues on this row. Credit: Paul Farrow] + +; [To fix the bug, the POP HL and JR NC,#15CB (ROM 0) instructions following the call to #2E41 (ROM 0) should be replaced with the following. Credit: Paul Farrow. +; +; PUSH DE ; DE=Address of the start of the row of the BASIC line in the Screen Line Edit Buffer. +; PUSH AF ; Save the flags. +; +; LD HL,#0020 ; +; ADD HL,DE ; +; EX DE,HL ; DE=Address of the flag byte for the row in the Screen Line Edit Buffer. +; +; POP AF ; Restore the flags. +; JR C,CHAR_FOUND ; Jump if editable column found. +; +; LD A,(DE) ; Fetch the flag byte. +; BIT 1,A ; Does the BASIC line span onto the next row? +; JR NZ,SPANS_ROW ; Jump if it does. +; +; POP DE ; DE=Address of the start of the BASIC row in the Screen Line Edit Buffer. +; POP HL +; LD HL,#0000 ; Signal no editable characters left on the row. +; LD (#EC06),HL ; +; JP #15C0 (ROM 0) ; Jump since all characters on the row have been scanned through. +; +;SPANS_ROW: +; POP DE ; DE=Address of the start of the BASIC row in the Screen Line Edit Buffer. +; POP HL ; +; JP #15CB (ROM 0) ; Jump if no editable columns left on the row. +; +;CHAR_FOUND: +; POP DE ; DE=Address of the start of the BASIC row in the Screen Line Edit Buffer. +; POP HL ; ] + + PUSH HL ; + CALL L2E41 ; Find editable position on this row from the previous column to the right, returning column number in B. + POP HL ; + JR NC,L15CB ; Jump if no editable character found on this row, i.e. there must be more characters on the next row. + +;An editable character was found to the right on the current row + + DEC HL ; Decrement the count of characters prior to the cursor. + INC B ; Advance to next column. + LD (#EC06),HL ; Update the count of the number of editable characters up to the cursor. + JR L15AB ; Jump back to test next column. + +;Column position of cursor located, find the closest editable character + +L15C0: CALL L2E41 ; Find editable position on this row from the previous column to the right, returning column number in B. + CALL NC,L2E63 ; If no editable character found then find editable position to the left, returning column number in B. + + LD HL,#EEF5 ; Flags. + LD (HL),#00 ; Signal 'not the current line', 'current line has previously been found' and 'update display file enabled'. + +;Store the current cursor position + +L15CB: LD A,B ; A=Column number. This will be the preferred column number. + POP BC ; B=Row number, C=Number of editing rows on screen. + PUSH BC ; + LD C,B ; C=Row number. + LD B,A ; B=Column number. + CALL L2A11 ; Store this as the current cursor editing position. + +;Move to next row + +L15D3: POP BC ; B=Row number, C=Number of editing rows on screen. + POP DE ; DE=Start address in Screen Line Edit Buffer of the next row. + LD A,C ; A=Number of editing rows on screen. + INC B ; Next row. + CP B ; Reached the bottom screen row? + JR NC,L156F ; Jump back if not to display the next row. + +;The bottom screen row has been exceeded + + LD A,(#EEF5) ; Listing flags. + BIT 1,A ; Has the current line been previously found? + JR Z,L1602 ; Jump if so. + +;Current line has not yet been found + + BIT 0,A ; Is this the current line? + JR NZ,L1602 ; Jump if so. + +;This is not the current line + + LD HL,(#5C49) ; E_PPC. Current line number. + LD A,H ; + OR L ; + JR Z,L15F4 ; Jump if there is no current line number. + + LD (#FC9A),HL ; Store it as the line number at top of the screen. + + CALL L3222 ; Set default Above-Screen Line Edit Buffer settings to clear the count of the number of rows it contains. + JR L15FD ; Jump forward. + +;There is no current line number + +L15F4: LD (#FC9A),HL ; Set the line number at top of the screen to #0000, i.e. first available. + CALL L3352 ; Create line number representation in the Keyword Construction Buffer of the next BASIC line. + LD (#5C49),HL ; E_PPC. Current line number is the first in the BASIC program. + +L15FD: POP DE ; DE=Start address in Screen Line Edit Buffer of the first row. + POP BC ; B=Row number, C=Number of editing rows on screen. + JP L1536 ; Jump back to continue listing the program until the current line is found. + +;The bottom line is the current line + +L1602: POP DE ; DE=Start address in Screen Line Edit Buffer of the first row. + POP BC ; B=Row number, C=Number of editing rows on screen. + CP A ; Set the zero flag if current line has yet to be found, hence signal do not update cursor position settings. + +; ---------------------------------------------------------- +; Print All Screen Line Edit Buffer Rows to the Display File +; ---------------------------------------------------------- +; Print all rows of the edit buffer to the display file, and updating the cursor position settings if required. +; Entry: Zero flag reset if update of cursor position settings required. +; B=Row number. +; C=Number of editing rows on screen. + +L1605: PUSH AF ; Save the zero flag. + + LD A,C ; Save the number of editing rows on screen. + LD C,B ; C=Row number. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of row held in C + EX DE,HL ; and transfer into HL. + +L160C: PUSH AF ; A=Number of editing rows on screen. + CALL L3604 ; Print a row of the edit buffer to the screen. + POP AF ; + + LD DE,#0023 ; + ADD HL,DE ; Point to the start of the next row. + +L1615: INC C ; Advance to the next row. + CP C ; All rows printed? + JR NC,L160C ; Jump back if not to print next row. + +;All rows printed + + POP AF ; Retrieve the zero flag. + RET Z ; Return if 'not the current line' and 'current line has previously been found'. + +;Find the new cursor column position + + CALL L2A07 ; Get current cursor position (C=row, B=column, A=preferred column). + +L161E: CALL L2B78 ; Find next Screen Line Edit Buffer editable position to right, moving to next row if necessary. Returns column number in B. + + LD HL,(#EC06) ; Fetch the number of editable characters on this row prior to the cursor. + DEC HL ; Decrement the count. + LD A,H ; Are there any characters? + OR L ; + LD (#EC06),HL ; Store the new count. + JR NZ,L161E ; Jump if there are some characters prior to the cursor. + + JP L2A11 ; Store cursor editing position, with preferred column of 0. + + RET ; [Redundant byte] + +; --------------------- +; Clear Editing Display +; --------------------- + +L1630: LD B,#00 ; Top row of editing area. + LD A,(#EC15) ; The number of editing rows on screen. + LD D,A ; D=Number of rows in editing area. + JP L3B5E ; Clear specified display rows. + +; ----------------------------------------------------------------- +; Shift All Edit Buffer Rows Up and Update Display File if Required +; ----------------------------------------------------------------- +; This routine shifts all edit buffer rows up, updating the display file if required. +; Entry: HL=Address of the 'Bottom Row Scroll Threshold' within the editing area information. +; Exit : Carry flag set if edit buffer rows were shifted. + +L1639: LD B,#00 ; Row number to start shifting from. + PUSH HL ; Save the address of the 'Bottom Row Scroll Threshold' within the editing area information. + +;Attempt to shift a row into the Above-Screen Line Edit Buffer + + LD C,B ; Find the address of row 0. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + CALL L326A ; Attempt to shift the top row of the Screen Line Edit Buffer into the Above-Screen Line Edit Buffer. + + POP HL ; Retrieve the address of the 'Bottom Row Scroll Threshold' within the editing area information. + RET NC ; Return if the Above-Screen Line Edit Buffer is full, i.e. no edit buffer rows shifted. + +;A change to the number of rows in the Above-Screen Line Edit Buffer occurred + + CALL L30DF ; Shift up rows of the BASIC line in Below-Screen Line Edit Buffer, inserting the next line BASIC line if the buffer becomes empty. + ; Returns with HL holding the address of the first row in the Below-Screen Line Edit Buffer. + +; Shift All Screen Line Edit Buffer Rows Up and Update Display File if Required +; ----------------------------------------------------------------------------- + +L1648: PUSH BC ; B=Row counter. + PUSH HL ; HL=Address of first row in the Below-Screen Line Edit Buffer. + + LD HL,#0023 ; DE=Address of the current row in the Screen Line Edit Buffer. + ADD HL,DE ; HL=Address of the next row in the Screen Line Edit Buffer. + + LD A,(#EC15) ; + LD C,A ; C=Number of editing rows on screen. + CP B ; Any rows to shift? + JR Z,L1663 ; Jump if not. + +;Shift all Screen Line Edit Buffer rows up + + PUSH BC ; C=Number of editing rows on screen. + +L1656: PUSH BC ; C=Number of editing rows on screen. + LD BC,#0023 ; DE=Current Screen Line Edit Buffer row, HL=Next Screen Line Edit Buffer row. + LDIR ; Shift one row of the Screen Line Edit Buffer up. + POP BC ; C=Number of editing rows on screen. + + LD A,C ; Fetch the number of editing rows on screen. + INC B ; Next row. + CP B ; All rows shifted? + JR NZ,L1656 ; Repeat for all edit buffer rows to shift. + +;All Screen Line Edit Buffer rows have been shifted up + + POP BC ; C=Number of editing rows on screen, B=Row number, i.e. 0. + +L1663: POP HL ; HL=Address of the first row in the Below-Screen Line Edit Buffer. + +L1664: CALL L3618 ; Shift up all edit rows in the display file if updating required. + + LD BC,#0023 ; HL=Address of the first row in the Below-Screen Line Edit Buffer, DE=Address of last row in Screen Line Edit Buffer. + LDIR ; Copy the first row of the Below-Screen Line Edit Buffer into the last row of the Screen Line Edit Buffer. + + SCF ; Signal that edit buffer rows were shifted. + POP BC ; B=Row counter. + RET ; + +; ------------------------------------------------------------------- +; Shift All Edit Buffer Rows Down and Update Display File if Required +; ------------------------------------------------------------------- +; This routine shifts all edit buffer rows down, updating the display file if required. +; Exit : Carry flag set if edit buffer rows were shifted. +; B=Last row number to shift. + +;Shift all rows in the Above-Screen Line Edit Buffer, shifting in a new BASIC line if applicable + +L166F: LD B,#00 ; Last row number to shift. + CALL L322B ; Attempt to shift down the Above-Screen Line Edit Buffer, loading in a new BASIC line if it is empty. + RET NC ; Return if Above-Screen Line Edit Buffer is empty, i.e. no edit buffer rows were shifted. + +;Entry point from routine at #2ED3 (ROM 0) to insert a blank row + +L1675: PUSH BC ; B=Last row number to shift. + PUSH HL ; HL=Address of next row to use within the Above-Screen Line Edit Buffer. + +;Shift all rows in the Below-Screen Line Edit Buffer down, shifting in a new BASIC line if applicable + + LD A,(#EC15) ; A=Number of editing rows on screen. + LD C,A ; C=Number of editing rows on screen. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the last editing row. + + CALL L311E ; Shift down all rows in the Below-Screen Line Edit Buffer, or empty the buffer a row does not straggle off the bottom of the screen. + JR NC,L16A9 ; Jump if the Below-Screen Line Edit Buffer is full. + + DEC DE ; DE=Address of the last flag byte of the penultimate editing row in the Screen Line Edit Buffer. + LD HL,#0023 ; Length of an edit buffer row. + ADD HL,DE ; HL=Address of the last flag byte of the last editing row in the Screen Line Edit Buffer. + EX DE,HL ; DE=Address of last flag byte of last editing row in Screen Line Edit Buffer, HL=Address of last flag byte of penultimate editing row in Screen Line Edit Buffer. + + PUSH BC ; C=Number of editing rows on screen, B=Last row number to shift. + LD A,B ; + CP C ; Any rows to shift? + JR Z,L169A ; Jump if not. + +L168E: PUSH BC ; C=Row number to shift, B=Last row number to shift. + LD BC,#0023 ; + LDDR ; Copy one row of the Screen Line Edit Buffer down. + POP BC ; C=Number of editing rows on screen, B=Row shift counter. + +L1695: LD A,B ; A=Row shift counter. + DEC C ; + CP C ; + JR C,L168E ; Repeat for all edit buffer rows to shift. + +;All Screen Line Edit Buffer rows have been shifted down + +L169A: EX DE,HL ; HL=Address of last flag byte of first editing row in Screen Line Edit Buffer, DE=Address of byte before start of first editing row in Screen Line Edit Buffer. + INC DE ; DE=Start of first row in Screen Line Edit Buffer. + + POP BC ; C=Number of editing rows on screen, B=Last row number to shift. + POP HL ; HL=Address of next row to use within the Above-Screen Line Edit Buffer. + + CALL L362C ; Shift down all edit rows in the display file if updating required. + + LD BC,#0023 ; + LDIR ; Copy the next row of the Above-Screen Line Edit Buffer into the first row of the Screen Line Edit Buffer. + + SCF ; Signal Below-Screen Line Edit Buffer is not full. + POP BC ; B=Last row number to shift. + RET ; + +;The Below-Screen Line Edit Buffer is full + +L16A9: POP HL ; Restore registers. + POP BC ; B=Last row number to shift. + RET ; + +; --------------------------------------------------------- +; Insert Character into Edit Buffer Row, Shifting Row Right +; --------------------------------------------------------- +; This routine shifts a byte into an edit buffer row, shifting all existing +; characters right until either the end of the row is reached or the specified +; end column is reached. +; Entry: DE=Start address of an edit buffer row. +; A=Character to shift into left of row. +; B=Column to start shifting at. +; Exit : A=Byte shifted out from last column. +; HL=Points byte after row (i.e. flag byte). +; Zero flag set if the character shifted out was a null (#00). + +L16AC: PUSH DE ; Save DE. + + LD H,#00 ; + LD L,B ; HL=Start column number. + +L16B0: ADD HL,DE ; HL=Address of the starting column. + LD D,A ; Store the character to shift in. + LD A,B ; A=Start column number. + +;Shift all bytes in the row to the right. + +L16B3: LD E,(HL) ; Fetch a character from the row. + LD (HL),D ; Replace it with the character to shift in. + LD D,E ; Store the old character for use next time. + + INC HL ; Point to the next column. + INC A ; + CP #20 ; End of row reached? + JR C,L16B3 ; Jump if not to shift the next character. + + LD A,E ; A=Character that was shifted out. + CP #00 ; Return with zero flag set if the character was #00. + + POP DE ; Restore DE + RET ; + +; -------------------------------------------------------- +; Insert Character into Edit Buffer Row, Shifting Row Left +; -------------------------------------------------------- +; This routine shifts a byte into an edit buffer row, shifting all existing +; characters left until either the beginning of the row is reached or the specified +; end column is reached. +; Entry: DE=Start address of an edit buffer row. +; A=Character to shift into right of row. +; B=Column to stop shifting at. +; Exit : A=Byte shifted out. +; HL=Points byte before row. +; Zero flag set if the character shifted out was a null (#00). + +L16C1: PUSH DE ; Save DE. + + LD HL,#0020 ; 32 columns. + +L16C5: ADD HL,DE ; Point to the flag byte for this row. + PUSH HL ; Save it. + + LD D,A ; Store the character to shift in. + LD A,#1F ; Maximum of 31 shifts. + JR L16D3 ; Jump ahead to start shifting. + +L16CC: LD E,(HL) ; Fetch a character from the row. + LD (HL),D ; Replace it with the character to shift in. + LD D,E ; Store the old character for use next time. + CP B ; End column reached? + JR Z,L16D6 ; Jump if so to exit. + + DEC A ; Decrement column counter. + +L16D3: DEC HL ; Point back a column. + JR L16CC ; Loop back to shift the next character. + +L16D6: LD A,E ; A=Character that was shifted out. + CP #00 ; Return with zero flag set if the character was #00. + + POP HL ; Fetch address of next flag byte for the row. + POP DE ; Restore DE. + RET ; + + +; ======================================================= +; BASIC LINE AND COMMAND INTERPRETATION ROUTINES - PART 1 +; ======================================================= + +; ----------------------- +; The Syntax Offset Table +; ----------------------- +; Similar in construction to the table in ROM 1 at #1A48. +; [No instruction fetch at #1708 hence ZX Interface 1 will not be paged in by this ROM. Credit: Paul Farrow]. + +L16DC: DEFB #B1 ; DEF FN -> #178D (ROM 0) + DEFB #C9 ; CAT -> #17A6 (ROM 0) + DEFB #BC ; FORMAT -> #179A (ROM 0) + DEFB #BE ; MOVE -> #179D (ROM 0) + DEFB #C3 ; ERASE -> #17A3 (ROM 0) + DEFB #AF ; OPEN # -> #1790 (ROM 0) + DEFB #B4 ; CLOSE # -> #1796 (ROM 0) + DEFB #93 ; MERGE -> #1776 (ROM 0) + DEFB #91 ; VERIFY -> #1775 (ROM 0) + DEFB #92 ; BEEP -> #1777 (ROM 0) + DEFB #95 ; CIRCLE -> #177B (ROM 0) + DEFB #98 ; INK -> #177F (ROM 0) + DEFB #98 ; PAPER -> #1780 (ROM 0) + DEFB #98 ; FLASH -> #1781 (ROM 0) + DEFB #98 ; BRIGHT -> #1782 (ROM 0) + DEFB #98 ; INVERSE -> #1783 (ROM 0) + DEFB #98 ; OVER -> #1784 (ROM 0) + DEFB #98 ; OUT -> #1785 (ROM 0) + DEFB #7F ; LPRINT -> #176D (ROM 0) + DEFB #81 ; LLIST -> #1770 (ROM 0) + DEFB #2E ; STOP -> #171E (ROM 0) + DEFB #6C ; READ -> #175D (ROM 0) + DEFB #6E ; DATA -> #1760 (ROM 0) + DEFB #70 ; RESTORE -> #1763 (ROM 0) + DEFB #48 ; NEW -> #173C (ROM 0) + DEFB #94 ; BORDER -> #1789 (ROM 0) + DEFB #56 ; CONTINUE -> #174C (ROM 0) + DEFB #3F ; DIM -> #1736 (ROM 0) + DEFB #41 ; REM -> #1739 (ROM 0) + DEFB #2B ; FOR -> #1724 (ROM 0) + DEFB #17 ; GO TO -> #1711 (ROM 0) + DEFB #1F ; GO SUB -> #171A (ROM 0) + DEFB #37 ; INPUT -> #1733 (ROM 0) + DEFB #77 ; LOAD -> #1774 (ROM 0) + DEFB #44 ; LIST -> #1742 (ROM 0) + DEFB #0F ; LET -> #170E (ROM 0) + DEFB #59 ; PAUSE -> #1759 (ROM 0) + DEFB #2B ; NEXT -> #172C (ROM 0) + DEFB #43 ; POKE -> #1745 (ROM 0) + DEFB #2D ; PRINT -> #1730 (ROM 0) + DEFB #51 ; PLOT -> #1755 (ROM 0) + DEFB #3A ; RUN -> #173F (ROM 0) + DEFB #6D ; SAVE -> #1773 (ROM 0) + DEFB #42 ; RANDOMIZE -> #1749 (ROM 0) + DEFB #0D ; IF -> #1715 (ROM 0) + DEFB #49 ; CLS -> #1752 (ROM 0) + DEFB #5C ; DRAW -> #1766 (ROM 0) + DEFB #44 ; CLEAR -> #174F (ROM 0) + DEFB #15 ; RETURN -> #1721 (ROM 0) + DEFB #5D ; COPY -> #176A (ROM 0) + +; -------------------------- +; The Syntax Parameter Table +; -------------------------- +; Similar to the parameter table in ROM 1 at #1A7A. + +L170E: DEFB #01 ; CLASS-01 LET + DEFB '=' ; #3D. '=' + DEFB #02 ; CLASS-02 + +L1711: DEFB #06 ; CLASS-06 GO TO + DEFB #00 ; CLASS-00 + DEFW GO_TO ; #1E67. GO TO routine in ROM 1. + +L1715: DEFB #06 ; CLASS-06 IF + DEFB #CB ; 'THEN' + DEFB #0E ; CLASS-0E + DEFW L1967 ; New IF routine in ROM 0. + +L171A: DEFB #06 ; CLASS-06 GO SUB + DEFB #0C ; CLASS-0C + DEFW L1A53 ; New GO SUB routine in ROM 0. + +L171E: DEFB #00 ; CLASS-00 STOP + DEFW STOP ; #1CEE. STOP routine in ROM 1. + +L1721: DEFB #0C ; CLASS-0C RETURN + DEFW L1A6F ; New RETURN routine in ROM 0. + +L1724: DEFB #04 ; CLASS-04 FOR + DEFB '=' ; #3D. '=' + DEFB #06 ; CLASS-06 + DEFB #CC ; 'TO' + DEFB #06 ; CLASS-06 + DEFB #0E ; CLASS-0E + DEFW L1981 ; New FOR routine in ROM 0. + +L172C: DEFB #04 ; CLASS-04 NEXT + DEFB #00 ; CLASS-00 + DEFW NEXT ; #1DAB. NEXT routine in ROM 1. + +L1730: DEFB #0E ; CLASS-0E PRINT + DEFW L2178 ; New PRINT routine in ROM 0. + +L1733: DEFB #0E ; CLASS-0E INPUT + DEFW L218C ; New INPUT routine in ROM 0. + +L1736: DEFB #0E ; CLASS-0E DIM + DEFW L21D5 ; New DIM routine in ROM 0. + +L1739: DEFB #0E ; CLASS-0E REM + DEFW L1862 ; New REM routine in ROM 0. + +L173C: DEFB #0C ; CLASS-0C NEW + DEFW L21AA ; New NEW routine in ROM 0. + +L173F: DEFB #0D ; CLASS-0D RUN + DEFW L1A02 ; New RUN routine in ROM 0. + +L1742: DEFB #0E ; CLASS-0E LIST + DEFW L1B75 ; New LIST routine in ROM 0. + +L1745: DEFB #08 ; CLASS-08 POKE + DEFB #00 ; CLASS-00 + DEFW POKE ; #1E80. POKE routine in ROM 1. + +L1749: DEFB #03 ; CLASS-03 RANDOMIZE + DEFW RANDOMIZE ; #1E4F. RANDOMIZE routine in ROM 1. + +L174C: DEFB #00 ; CLASS-00 CONTINUE + DEFW CONTINUE ; #1E5F. CONTINUE routine in ROM 1. + +L174F: DEFB #0D ; CLASS-0D CLEAR + DEFW L1A0D ; New CLEAR routine in ROM 0. + +L1752: DEFB #00 ; CLASS-00 CLS + DEFW CLS ; #0D6B. CLS routine in ROM 1. + +L1755: DEFB #09 ; CLASS-09 PLOT + DEFB #00 ; CLASS-00 + DEFW PLOT ; #22DC. PLOT routine in ROM 1 + +L1759: DEFB #06 ; CLASS-06 PAUSE + DEFB #00 ; CLASS-00 + DEFW PAUSE ; #1F3A. PAUSE routine in ROM 1. + +L175D: DEFB #0E ; CLASS-0E READ + DEFW L19AB ; New READ routine in ROM 0. + +L1760: DEFB #0E ; CLASS-0E DATA + DEFW L19EB ; New DATA routine in ROM 0. + +L1763: DEFB #03 ; CLASS-03 RESTORE + DEFW RESTORE ; #1E42. RESTORE routine in ROM 1. + +L1766: DEFB #09 ; CLASS-09 DRAW + DEFB #0E ; CLASS-0E + DEFW L21BE ; New DRAW routine in ROM 0. + +L176A: DEFB #0C ; CLASS-0C COPY + DEFW L21A7 ; New COPY routine in ROM 0. + +L176D: DEFB #0E ; CLASS-0E LPRINT + DEFW L2174 ; New LPRINT routine in ROM 0. + +L1770: DEFB #0E ; CLASS-0E LLIST + DEFW L1B71 ; New LLIST routine in ROM 0. + +L1773: DEFB #0B ; CLASS-0B SAVE + +L1774: DEFB #0B ; CLASS-0B LOAD + +L1775: DEFB #0B ; CLASS-0B VERIFY + +L1776: DEFB #0B ; CLASS-0B MERGE + +L1777: DEFB #08 ; CLASS-08 BEEP + DEFB #00 ; CLASS-00 + DEFW BEEP ; #03F8. BEEP routine in ROM 1. + +L177B: DEFB #09 ; CLASS-09 CIRCLE + DEFB #0E ; CLASS-0E + DEFW L21AE ; New CIRCLE routine in ROM 0. + +L177F: DEFB #07 ; CLASS-07 INK + +L1780: DEFB #07 ; CLASS-07 PAPER + +L1781: DEFB #07 ; CLASS-07 FLASH + +L1782: DEFB #07 ; CLASS-07 BRIGHT + +L1783: DEFB #07 ; CLASS-07 INVERSE + +L1784: DEFB #07 ; CLASS-07 OVER + +L1785: DEFB #08 ; CLASS-08 OUT + DEFB #00 ; CLASS-00 + DEFW COUT ; #1E7A. OUT routine in ROM 1. + +L1789: DEFB #06 ; CLASS-06 BORDER + DEFB #00 ; CLASS-00 + DEFW BORDER ; #2294. BORDER routine in ROM 1. + +L178D: DEFB #0E ; CLASS-0E DEF FN + DEFW L1A8C ; New DEF FN routine in ROM 0. + +L1790: DEFB #06 ; CLASS-06 OPEN # + DEFB ',' ; #2C. ',' + DEFB #0A ; CLASS-0A + DEFB #00 ; CLASS-00 + DEFW OPEN ; #1736. OPEN # routine in ROM 1. + +L1796: DEFB #06 ; CLASS-06 CLOSE # + DEFB #00 ; CLASS-00 + DEFW CLOSE ; #16E5. CLOSE # routine in ROM 1. + +L179A: DEFB #0E ; CLASS-0E FORMAT + DEFW L0641 ; FORMAT routine in ROM 0. + +L179D: DEFB #0A ; CLASS-0A MOVE + DEFB ',' ; #2C. ',' + DEFB #0A ; CLASS-0A + DEFB #0C ; CLASS-0C + DEFW L1AF0 ; Just execute a RET. + +L17A3: DEFB #0E ; CLASS-0E ERASE + DEFW L1C0C ; New ERASE routine in ROM 0. + +L17A6: DEFB #0E ; CLASS-0E CAT + DEFW L1BE5 ; New CAT routine in ROM 0. + +L17A9: DEFB #0C ; CLASS-0C SPECTRUM + DEFW L1B2B ; SPECTRUM routine in ROM 0. + +L17AC: DEFB #0E ; CLASS-0E PLAY + DEFW L2317 ; PLAY routine in ROM 0. + +; (From Logan & O'Hara's 48K ROM disassembly): +; The requirements for the different command classes are as follows: +; CLASS-00 - No further operands. +; CLASS-01 - Used in LET. A variable is required. +; CLASS-02 - Used in LET. An expression, numeric or string, must follow. +; CLASS-03 - A numeric expression may follow. Zero to be used in case of default. +; CLASS-04 - A single character variable must follow. +; CLASS-05 - A set of items may be given. +; CLASS-06 - A numeric expression must follow. +; CLASS-07 - Handles colour items. +; CLASS-08 - Two numeric expressions, separated by a comma, must follow. +; CLASS-09 - As for CLASS-08 but colour items may precede the expressions. +; CLASS-0A - A string expression must follow. +; CLASS-0B - Handles cassette/RAM disk routines. + +; In addition the 128 adds the following classes: +; CLASS-0C - Like class 00 but calling ROM 0. (Used by SPECTRUM, MOVE, COPY, NEW, GO SUB, RETURN) +; CLASS-0D - Like class 06 but calling ROM 0. (Used by CLEAR, RUN) +; CLASS-0E - Handled in ROM 0. (Used by PLAY, ERASE, CAT, FORMAT, CIRCLE, LPRINT, LLIST, DRAW, DATA, READ, LIST, DIM, INPUT, PRINT, FOR, IF) + +; ------------------------------------------ +; The 'Main Parser' Of the BASIC Interpreter +; ------------------------------------------ +; The parsing routine of the BASIC interpreter is entered at #17AF (ROM 0) when syntax is being checked, +; and at #1838 (ROM 0) when a BASIC program of one or more statements is to be executed. +; This code is similar to that in ROM 1 at #1B17. + +L17AF: RES 7,(IY+#01) ; FLAGS. Signal 'syntax checking'. + RST #28 ; + DEFW E_LINE_NO ; #19FB. CH-ADD is made to point to the first code after any line number + XOR A ; + LD (#5C47),A ; SUBPPC. Set to #00. + DEC A ; + LD (#5C3A),A ; ERR_NR. Set to #FF. + JR L17C1 ; Jump forward to consider the first statement of the line. + +; ------------------ +; The Statement Loop +; ------------------ +; Each statement is considered in turn until the end of the line is reached. + +L17C0: RST #20 ; Advance CH-ADD along the line. + +L17C1: RST #28 ; + DEFW SET_WORK ; #16BF. The work space is cleared. + INC (IY+#0D) ; SUBPPC. Increase SUBPPC on each passage around the loop. + JP M,L1912 ; Only '127' statements are allowed in a single line. Jump to report "C Nonsense in BASIC". + + RST #18 ; Fetch a character. + LD B,#00 ; Clear the register for later. + CP #0D ; Is the character a 'carriage return'? + JP Z,L1863 ; jump if it is. + + CP ':' ; #3A. Go around the loop again if it is a ':'. + JR Z,L17C0 ; + +;A statement has been identified so, first, its initial command is considered + + LD HL,L1821 ; Pre-load the machine stack with the return address. + PUSH HL ; + + LD C,A ; Save the command temporarily + RST #20 ; in the C register whilst CH-ADD is advanced again. + LD A,C ; + SUB #CE ; Reduce the command's code by #CE giving the range indexed from #00. + JR NC,L17F4 ; Jump for DEF FN and above. + + ADD A,#CE ; + LD HL,L17A9 ; + CP #A3 ; Is it 'SPECTRUM'? + JR Z,L1800 ; Jump if so into the scanning loop with this address. + + LD HL,L17AC ; + CP #A4 ; Is it 'PLAY'? + JR Z,L1800 ; Jump if so into the scanning loop with this address. + + JP L1912 ; Produce error report "C Nonsense in BASIC". + +L17F4: LD C,A ; Move the command code to BC (B holds #00). + LD HL,L16DC ; The base address of the syntax offset table. + ADD HL,BC ; + LD C,(HL) ; + ADD HL,BC ; Find address for the command's entries in the parameter table. + JR L1800 ; Jump forward into the scanning loop with this address. + +;Each of the command class routines applicable to the present command are executed in turn. +;Any required separators are also considered. + +L17FD: LD HL,(#5C74) ; T_ADDR. The temporary pointer to the entries in the parameter table. + +L1800: LD A,(HL) ; Fetch each entry in turn. + INC HL ; Update the pointer to the entries for the next pass. + LD (#5C74),HL ; T_ADDR. + + LD BC,L17FD ; Pre-load the machine stack with the return address. + PUSH BC ; + + LD C,A ; Copy the entry to the C register for later. + CP #20 ; + JR NC,L181A ; Jump forward if the entry is a 'separator'. + + LD HL,L18B5 ; The base address of the 'command class' table. + LD B,#00 ; + ADD HL,BC ; Index into the table. + LD C,(HL) ; + ADD HL,BC ; HL=base + code + (base + code). + PUSH HL ; HL=The starting address of the required command class routine. + + RST #18 ; Before making an indirect jump to the command class routine pass the command code + DEC B ; to the A register and set the B register to #FF. + RET ; Return to the stacked address. + +; -------------------------- +; The 'Separator' Subroutine +; -------------------------- +; The report 'Nonsense in BASIC is given if the required separator is not present. +; But note that when syntax is being checked the actual report does not appear on the screen - only the 'error marker'. +; This code is similar to that in ROM 1 at #1B6F. + +L181A: RST #18 ; The current character is + CP C ; fetched and compared to the entry in the parameter table. + JP NZ,L1912 ; Give the error report if there is not a match. + + RST #20 ; Step past a correct character + RET ; and return. + +; --------------------------------- +; The 'Statement Return' Subroutine +; --------------------------------- +; After the correct interpretation of a statement, a return is made to this entry point. +; This code is similar to that in ROM 1 at #1B76. + +L1821: CALL L05D6 ; Check for BREAK + JR C,L182A ; Jump if pressed. + + CALL L05AC ; Produce error report. + DEFB #14 ; "L Break into program" + +L182A: BIT 7,(IY+#0A) ; NSPPC - statement number in line to be jumped to + JP NZ,L18A8 ; Jump forward if there is not a 'jump' to be made. + + LD HL,(#5C42) ; NEWPPC, line number to be jumped to. + BIT 7,H ; + JR Z,L184C ; Jump forward unless dealing with a further statement in the editing area. + +; -------------------------- +; The 'Line Run' Entry Point +; -------------------------- +; This entry point is used wherever a line in the editing area is to be 'run'. +; In such a case the syntax/run flag (bit 7 of FLAGS) will be set. +; The entry point is also used in the syntax checking of a line in the editing area +; that has more than one statement (bit 7 of FLAGS will be reset). +; This code is similar to that in ROM 1 at #1B8A. + +L1838: LD HL,#FFFE ; A line in the editing area is considered as line '-2'. + LD (#5C45),HL ; PPC. + LD HL,(#5C61) ; WORKSP. Make HL point to the end marker of the editing area. + DEC HL ; + LD DE,(#5C59) ; E_LINE. Make DE point to the location before the end marker of the editing area. + DEC DE ; + LD A,(#5C44) ; NSPPC. Fetch the number of the next statement to be handled. + JR L1882 ; Jump forward. + +; ------------------------- +; The 'Line New' Subroutine +; ------------------------- +; There has been a jump in the program and the starting address of the new line has to be found. +; This code is similar to that in ROM 1 at 1B9E. + +L184C: RST #28 ; + DEFW LINE_ADDR ; #196E. The starting address of the line, or the 'first line after' is found. + LD A,(#5C44) ; NSPPC. Collect the statement number. + JR Z,L1870 ; Jump forward if the required line was found. + + AND A ; Check the validity of the statement number - must be zero. + JR NZ,L189D ; Jump if not to produce error report "N Statement lost". + + LD B,A ; Also check that the 'first + LD A,(HL) ; line after' is not after the + AND #C0 ; actual 'end of program'. + LD A,B ; + JR Z,L1870 ; Jump forward with valid addresses; otherwise signal the error 'OK'. + + CALL L05AC ; Produce error report. + DEFB #FF ; "0 OK" + +; ----------- +; REM Routine +; ----------- +; The return address to STMT-RET is dropped which has the effect of forcing the rest of the +; line to be ignored. +; This code is similar to that in ROM 1 at #1BB2. + +L1862: POP BC ; Drop the statement return address. + +; ---------------------- +; The 'Line End' Routine +; ---------------------- +; If checking syntax a simple return is made but when 'running' the address held by NXTLIN +; has to be checked before it can be used. +; This code is similar to that in ROM 1 at #1BB3. + +L1863: BIT 7,(IY+#01) ; + RET Z ; Return if syntax is being checked. + + LD HL,(#5C55) ; NXTLIN. + LD A,#C0 ; Return if the address is after the end of the program - the 'run' is finished. + AND (HL) ; + RET NZ ; + + XOR A ; Signal 'statement zero' before proceeding. + +; ---------------------- +; The 'Line Use' Routine +; ---------------------- +; This routine has three functions: +; i. Change statement zero to statement '1'. +; ii. Find the number of the new line and enter it into PPC. +; iii. Form the address of the start of the line after. +; This code is similar to that in ROM 1 at #1BBF. + +L1870: CP #01 ; Statement zero becomes statement 1. + ADC A,#00 ; + LD D,(HL) ; The line number of the line to be used is collected and + INC HL ; passed to PPC. + LD E,(HL) ; + LD (#5C45),DE ; PPC. + INC HL ; + LD E,(HL) ; Now find the 'length' of the line. + INC HL ; + LD D,(HL) ; + EX DE,HL ; Switch over the values. + ADD HL,DE ; Form the address of the start of the line after in HL and the + INC HL ; location before the 'next' line's first character in DE. + +; ----------------------- +; The 'Next Line' Routine +; ----------------------- +; On entry the HL register pair points to the location after the end of the 'next' line +; to be handled and the DE register pair to the location before the first character of the line. +; This applies to lines in the program area and also to a line in the editing area - where the +; next line will be the same line again whilst there are still statements to be interpreted. +; This code is similar to that in ROM 1 at #1BD1. + +L1882: LD (#5C55),HL ; NXTLIN. Set NXTLIN for use once the current line has been completed. + EX DE,HL ; + LD (#5C5D),HL ; CH_ADD. CH_ADD points to the location before the first character to be considered. + LD D,A ; The statement number is fetched. + LD E,#00 ; The E register is cleared in case the 'Each Statement' routine is used. + LD (IY+#0A),#FF ; NSPPC. Signal 'no jump'. + DEC D ; + LD (IY+#0D),D ; SUB_PPC. Statement number-1. + JP Z,L17C0 ; Jump if the first statement. + + INC D ; For later statements the 'starting address' has to be found. + RST #28 ; + DEFW EACH_STMT ; #198B. + JR Z,L18A8 ; Jump forward unless the statement does not exist. + +L189D: CALL L05AC ; Produce error report. + DEFB #16 ; "N Statement lost" + +; -------------------------- +; The 'CHECK-END' Subroutine +; -------------------------- +; This is called when the syntax of the edit-line is being checked. The purpose of the routine is to +; give an error report if the end of a statement has not been reached and to move on to the next +; statement if the syntax is correct. +; The routine is the equivalent of routine CHECK_END in ROM 1 at #1BEE. + +L18A1: BIT 7,(IY+#01) ; Very like CHECK-END at 1BEE in ROM 1 + RET NZ ; Return unless checking syntax. + + POP BC ; Drop scan loop and statement return addresses. + POP BC ; + +; ----------------------- +; The 'STMT-NEXT' Routine +; ----------------------- +; If the present character is a 'carriage return' then the 'next statement' is on the 'next line', +; if ':' it is on the same line; but if any other character is found then there is an error in syntax. +; The routine is the equivalent of routine STMT_NEXT in ROM 1 at #1BF4. + +L18A8: RST #18 ; Fetch the present character. + CP #0D ; Consider the 'next line' if + JR Z,L1863 ; it is a 'carriage return'. + + CP ':' ; #3A. Consider the 'next statement' + JP Z,L17C0 ; if it is a ':'. + + JP L1912 ; Otherwise there has been a syntax error so produce "C Nonsense in BASIC". + +; ------------------------- +; The 'Command Class' Table +; ------------------------- + +L18B5: DEFB L18D9-$ ; CLASS-00 -> L18D9 = #24 + DEFB L18F9-$ ; CLASS-01 -> L18F9 = #43 + DEFB L18FD-$ ; CLASS-02 -> L18FD = #46 + DEFB L18D6-$ ; CLASS-03 -> L18D6 = #1E + DEFB L1905-$ ; CLASS-04 -> L1905 = #4C + DEFB L18DA-$ ; CLASS-05 -> L18DA = #20 + DEFB L190E-$ ; CLASS-06 -> L190E = #53 + DEFB L191A-$ ; CLASS-07 -> L191A = #5E + DEFB L190A-$ ; CLASS-08 -> L190A = #4D + DEFB L1944-$ ; CLASS-09 -> L1944 = #86 + DEFB L1916-$ ; CLASS-0A -> L1916 = #57 + DEFB L1948-$ ; CLASS-0B -> L1948 = #88 + DEFB L18C7-$ ; CLASS-0C -> L18C7 = #06 + DEFB L18C4-$ ; CLASS-0D -> L18C4 = #02 + DEFB L18C8-$ ; CLASS-0E -> L18C8 = #05 + +; ----------------------------------- +; The 'Command Classes - 0C, 0D & 0E' +; ----------------------------------- +; For commands of class-0D a numeric expression must follow. + +L18C4: RST #28 ; Code 0D enters here. + DEFW FETCH_NUM ; #1CDE. + +;The commands of class-0C must not have any operands. e.g. SPECTRUM. + +L18C7: CP A ; Code 0C enters here. Set zero flag. + +;The commands of class-0E may be followed by a set of items. e.g. PLAY. + +L18C8: POP BC ; Code 0E enters here. + ; Retrieve return address. + CALL Z,L18A1 ; If handling commands of classes 0C & 0D and syntax is being + ; checked move on now to consider the next statement. + EX DE,HL ; Save the line pointer in DE. + +; After the command class entries and the separator entries in the parameter table have +; been considered the jump to the appropriate command routine is made. +; The routine is similar to JUMP-C-R in ROM 1 at #1C16. + + LD HL,(#5C74) ; T_ADDR. + LD C,(HL) ; Fetch the pointer to the entries in the parameter table + INC HL ; and fetch the address of the + LD B,(HL) ; required command routine. + EX DE,HL ; Exchange the pointers back. + PUSH BC ; Make an indirect jump to the command routine. + RET ; + +; ----------------------------------- +; The 'Command Classes - 00, 03 & 05' +; ----------------------------------- +; These routines are the equivalent of the routines in ROM 1 starting at #1C0D. + +; The commands of class-03 may, or may not, be followed by a number. e.g. RUN & RUN 200. + +L18D6: RST #28 ; Code 03 enters here. + DEFW FETCH_NUM ; #1CDE. A number is fetched but zero is used in cases of default. + +;The commands of class-00 must not have any operands. e.g. COPY & CONTINUE. + +L18D9: CP A ; Code 00 enters here. Set the zero flag. + +;The commands of class-05 may be followed by a set of items. e.g. PRINT & PRINT "222". + +L18DA: POP BC ; Code 05 enters here. Drop return address. + CALL Z,L18A1 ; If handling commands of classes 00 & 03 and syntax is being + ; checked move on now to consider the next statement. + EX DE,HL ; Save the line pointer in DE. + + LD HL,(#5C74) ; T_ADDR. Fetch the pointer to the entries in the parameter table. + LD C,(HL) ; + INC HL ; + LD B,(HL) ; Fetch the address of the required command routine. + EX DE,HL ; Exchange the pointers back. + PUSH HL ; Save command routine address. + + LD HL,L18F8 ; The address to return to (the RET below). + LD (RETADDR),HL ; #5B5A. Store the return address. + LD HL,YOUNGER ; #5B14. Paging subroutine. + EX (SP),HL ; Replace the return address with the address of the YOUNGER routine. + PUSH HL ; Save the original top stack item. + LD H,B ; + LD L,C ; HL=Address of command routine. + EX (SP),HL ; Put onto the stack so that an indirect jump will be made to it. + JP SWAP_ROM ; #5B00. Switch to other ROM and 'return' to the command routine. + +;Comes here after ROM 1 has been paged in, the command routine called, ROM 0 paged back in. + +L18F8: RET ; Simply make a return. + +; ------------------------ +; The 'Command Class - 01' +; ------------------------ +; Command class 01 is concerned with the identification of the variable in a LET, READ or INPUT statement. + +L18F9: RST #28 ; Delegate handling to ROM 1. + DEFW CLASS_01 ; #1C1F. + RET ; + +; ------------------------ +; The 'Command Class - 02' +; ------------------------ +; Command class 02 is concerned with the actual calculation of the value to be assigned in a LET statement. + +L18FD: POP BC ; Code 02 enters here. Delegate handling to ROM 1. + RST #28 ; + DEFW VAL_FET_1 ; #1C56. "... used by LET, READ and INPUT statements to + ; first evaluate and then assign values to the + ; previously designated variable" (Logan/O'Hara) + CALL L18A1 ; Move on to the next statement if checking syntax + RET ; else return here. + +; ------------------------ +; The 'Command Class - 04' +; ------------------------ +; The command class 04 entry point is used by FOR & NEXT statements. + +L1905: RST #28 ; Code 04 enters here. Delegate handling to ROM 1. + DEFW CLASS_04 ; #1C6C. + RET ; + +; ------------------------ +; The 'Command Class - 08' +; ------------------------ +; Command class 08 allows for two numeric expressions, separated by a comma, to be evaluated. + +L1909: RST #20 ; [Redundant byte] + +L190A: RST #28 ; Delegate handling to ROM 1. + DEFW EXPT_2NUM ; #1C7A. + RET ; + +; ------------------------ +; The 'Command Class - 06' +; ------------------------ +; Command class 06 allows for a single numeric expression to be evaluated. + +L190E: RST #28 ; Code 06 enters here. Delegate handling to ROM 1. + DEFW EXPT_1NUM ; #1C82. + RET ; + +; ---------------------------- +; Report C - Nonsense in BASIC +; ---------------------------- + +L1912: CALL L05AC ; Produce error report. [Could have saved 4 bytes by using the identical routine at #1219 (ROM 0) instead] + DEFB #0B ; "C Nonsense in BASIC" + +; ------------------------ +; The 'Command Class - 0A' +; ------------------------ +; Command class 0A allows for a single string expression to be evaluated. + +L1916: RST #28 ; Code 0A enters here. Delegate handling to ROM 1. + DEFW EXPT_EXP ; #1C8C. + RET ; + +; ------------------------ +; The 'Command Class - 07' +; ------------------------ +; Command class 07 is the command routine for the six colour item commands. +; Makes the current temporary colours permanent. + +L191A: BIT 7,(IY+#01) ; The syntax/run flag is read. + RES 0,(IY+#02) ; TV_FLAG. Signal 'main screen'. + JR Z,L1927 ; Jump ahead if syntax checking. + + RST #28 ; Only during a 'run' call TEMPS to ensure the temporary + DEFW TEMPS ; #0D4D. colours are the main screen colours. + +L1927: POP AF ; Drop the return address. + LD A,(#5C74) ; T_ADDR. + SUB (L177F & #00FF)+#28 ; Reduce to range #D9-#DE which are the token codes for INK to OVER. + RST #28 ; + DEFW CO_TEMP_4 ; #21FC. Change the temporary colours as directed by the BASIC statement. + CALL L18A1 ; Move on to the next statement if checking syntax. + + LD HL,(#5C8F) ; ATTR_T. Now the temporary colour + LD (#5C8D),HL ; ATTR_P. values are made permanent + LD HL,#5C91 ; P_FLAG. + LD A,(HL) ; Value of P_FLAG also has to be considered. + +;The following instructions cleverly copy the even bits of the supplied byte to the odd bits. +;In effect making the permanent bits the same as the temporary ones. + + RLCA ; Move the mask leftwards. + XOR (HL) ; Impress onto the mask + AND #AA ; only the even bits of the + XOR (HL) ; other byte. + LD (HL),A ; Restore the result. + RET ; + +; ------------------------ +; The 'Command Class - 09' +; ------------------------ +; This routine is used by PLOT, DRAW & CIRCLE statements in order to specify the default conditions +; of 'FLASH 8; BRIGHT 8; PAPER 8;' that are set up before any embedded colour items are considered. + +L1944: RST #28 ; Code 09 enters here. Delegate handling to ROM 1. + DEFW CLASS_09 ; #1CBE. + RET + +; ------------------------ +; The 'Command Class - 0B' +; ------------------------ +; This routine is used by SAVE, LOAD, VERIFY & MERGE statements. + +L1948: POP AF ; Drop the return address. + + LD A,(FLAGS3) ; #5B66. + AND #0F ; Clear LOAD/SAVE/VERIFY/MERGE indication bits. + LD (FLAGS3),A ; #5B66. + + LD A,(#5C74) ; T_ADDR-lo. + SUB 1+(L1773 & #00FF) ; Correct by #74 so that SAVE = #00, LOAD = #01, VERIFY = #02, MERGE = #03. + LD (#5C74),A ; T_ADDR-lo. + JP Z,L11EB ; Jump to handle SAVE. + + DEC A ; + JP Z,L11F2 ; Jump to handle LOAD. + + DEC A ; + JP Z,L11F9 ; Jump to handle VERIFY. + + JP L1200 ; Jump to handle MERGE. + +; ---------- +; IF Routine +; ---------- +; On entry the value of the expression between the IF and the THEN is the +; 'last value' on the calculator stack. If this is logically true then the next +; statement is considered; otherwise the line is considered to have been finished. + +L1967: POP BC ; Drop the return address. + BIT 7,(IY+#01) ; + JR Z,L197E ; Jump forward if checking syntax. + +;Now 'delete' the last value on the calculator stack + +L196E: LD HL,(#5C65) ; STKEND. + LD DE,#FFFB ; -5 + ADD HL,DE ; The present 'last value' is deleted. + LD (#5C65),HL ; STKEND. HL point to the first byte of the value. + RST #28 ; + DEFW TEST_ZERO ; #34E9. Is the value zero? + JP C,L1863 ; If the value was 'FALSE' jump to the next line. + +L197E: JP L17C1 ; But if 'TRUE' jump to the next statement (after the THEN). + +; ----------- +; FOR Routine +; ----------- +; This command routine is entered with the VALUE and the LIMIT of the FOR statement already +; on the top of the calculator stack. + +L1981: CP #CD ; Jump forward unless a 'STEP' is given. + JR NZ,L198E ; + + RST #20 ; Advance pointer + CALL L190E ; Indirectly call EXPT_1NUM in ROM 1 to get the value of the STEP. + CALL L18A1 ; Move on to the next statement if checking syntax. + JR L19A6 ; Otherwise jump forward. + +;There has not been a STEP supplied so the value '1' is to be used. + +L198E: CALL L18A1 ; Move on to the next statement if checking syntax. + LD HL,(#5C65) ; STKEND. + LD (HL),#00 ; + INC HL ; + LD (HL),#00 ; + INC HL ; + LD (HL),#01 ; + INC HL ; + LD (HL),#00 ; + INC HL ; + LD (HL),#00 ; Place a value of 1 on the calculator stack. + INC HL ; + LD (#5C65),HL ; STKEND. + +;The three values on the calculator stack are the VALUE (v), the LIMIT (l) and the STEP (s). +;These values now have to be manipulated. Delegate handling to ROM 1. + +L19A6: RST #28 ; + DEFW F_REORDER ; #1D16. + RET ; + +; ------------ +; READ Routine +; ------------ + +L19AA: RST #20 ; Come here on each pass, after the first, to move along the READ statement. + +L19AB: CALL L18F9 ; Indirectly call CLASS_01 in ROM 1 to consider whether the variable has + ; been used before, and find the existing entry if it has. + BIT 7,(IY+#01) ; + JR Z,L19E2 ; Jump forward if checking syntax. + + RST #18 ; Save the current pointer CH_ADD in X_PTR. + LD (#5C5F),HL ; X_PTR. + + LD HL,(#5C57) ; DATADD. + LD A,(HL) ; Fetch the current DATA list pointer + CP #2C ; and jump forward unless a new + JR Z,L19CB ; DATA statement has to be found. + + LD E,#E4 ; The search is for 'DATA'. + RST #28 ; + DEFW LOOK_PROG ; #1D86. + JR NC,L19CB ; Jump forward if the search is successful. + + CALL L05AC ; Produce error report. + DEFB #0D ; "E Out of Data" + +; Pick up a value from the DATA list. + +L19CB: INC HL ; Advance the pointer along the DATA list. + LD (#5C5D),HL ; CH_ADD. + LD A,(HL) ; + RST #28 ; + DEFW VAL_FET_1 ; #1C56. Fetch the value and assign it to the variable. + RST #18 ; + + LD (#5C57),HL ; DATADD. + LD HL,(#5C5F) ; X_PTR. Fetch the current value of CH_ADD and store it in DATADD. + + LD (IY+#26),#00 ; X_PTR_hi. Clear the address of the character after the '?' marker. + LD (#5C5D),HL ; CH_ADD. Make CH-ADD once again point to the READ statement. + LD A,(HL) ; + +L19E2: RST #18 ; GET the present character + CP ',' ; #2C. Check if it is a ','. + +L19E5: JR Z,L19AA ; If it is then jump back as there are further items. + + CALL L18A1 ; Return if checking syntax + RET ; or here if not checking syntax. + +; ------------ +; DATA Routine +; ------------ +; During syntax checking a DATA statement is checked to ensure that it contains a series +; of valid expressions, separated by commas. But in 'run-time' the statement is passed by. + +L19EB: BIT 7,(IY+#01) ; Jump forward unless checking syntax. + JR NZ,L19FC ; + +;A loop is now entered to deal with each expression in the DATA statement. + +L19F1: RST #28 ; + DEFW SCANNING ; #24FB. Scan the next expression. + CP ',' ; #2C. Check for the correct separator ','. + CALL NZ,L18A1 ; but move on to the next statement if not matched. + RST #20 ; Whilst there are still expressions to be checked + JR L19F1 ; go around again. + +;The DATA statement has to be passed-by in 'run-time'. + +L19FC: LD A,#E4 ; It is a 'DATA' statement that is to be passed-by. + +;On entry the A register will hold either the token 'DATA' or the token 'DEF FN' +;depending on the type of statement that is being 'passed-by'. + +L19FE: RST #28 ; + DEFW PASS_BY ; #1E39. Delegate handling to ROM 1. + RET + +; ----------- +; RUN Routine +; ----------- +; The parameter of the RUN command is passed to NEWPPC by calling the GO TO command routine. +; The operations of 'RESTORE 0' and 'CLEAR 0' are then performed before a return is made. + +L1A02: RST #28 + DEFW GO_TO ; #1E67. + + LD BC,#0000 ; Now perform a 'RESTORE 0'. + RST #28 + DEFW REST_RUN ; #1E45. + JR L1A10 ; Exit via the CLEAR command routine. + +; ------------- +; CLEAR Routine +; ------------- +; This routine allows for the variables area to be cleared, the display area cleared +; and RAMTOP moved. In consequence of the last operation the machine stack is rebuilt +; thereby having the effect of also clearing the GO SUB stack. + +L1A0D: RST #28 ; + DEFW FIND_INT2 ; #1E99. Fetch the operand - using zero by default. + +L1A10: LD A,B ; Jump forward if the operand is + OR C ; other than zero. When called + JR NZ,L1A18 ; from RUN there is no jump. + + LD BC,(#5CB2) ; RAMTOP. Use RAMTOP if the parameter is 0. + +L1A18: PUSH BC ; BC = Address to clear to. Save it. + LD DE,(#5C4B) ; VARS. + LD HL,(#5C59) ; E LINE. + DEC HL ; + RST #28 ; Delete the variables area. + DEFW RECLAIM ; #19E5. + RST #28 ; Clear the screen + DEFW CLS ; #0D6B. + +;The value in the BC register pair which will be used as RAMTOP is tested to ensure it +;is neither too low nor too high. + + LD HL,(#5C65) ; STKEND. The current value of STKEND + LD DE,#0032 ; is increased by 50 before + ADD HL,DE ; being tested. This forms the + POP DE ; ADE = address to clear to lower limit. + SBC HL,DE ; + JR NC,L1A3B ; Ramtop no good. + + LD HL,(#5CB4) ; P_RAMT. For the upper test the value + AND A ; for RAMTOP is tested against P_RAMT. + SBC HL,DE ; + JR NC,L1A3F ; Jump forward if acceptable. + +L1A3B: CALL L05AC ; Produce error report. + DEFB #15 ; "M Ramtop no good" + +L1A3F: LD (#5CB2),DE ; RAMTOP. + POP DE ; Retrieve interpreter return address from stack + POP HL ; Retrieve 'error address' from stack + POP BC ; Retrieve the GO SUB stack end marker. + ; [*BUG* - It is assumed that the top of the GO SUB stack will be empty and hence only + ; contain the end marker. This will not be the case if CLEAR is used within a subroutine, + ; in which case BC will now hold the calling line number and this will be stacked in place + ; of the end marker. When a RETURN command is encountered, the GO SUB stack appears to contain + ; an entry since the end marker was not the top item. An attempt to return is therefore made. + ; The CLEAR command handler within the 48K Spectrum ROM does not make any assumption about + ; the contents of the GO SUB stack and instead always re-inserts the end marker. The bug could + ; be fixed by inserting the line LD BC,#3E00 after the POP BC. Credit: Ian Collier (+3), Paul Farrow (128)] + LD SP,(#5CB2) ; RAMTOP. + INC SP ; + PUSH BC ; Stack the GO SUB stack end marker. + PUSH HL ; Stack 'error address'. + LD (#5C3D),SP ; ERR_SP. + PUSH DE ; Stack the interpreter return address. + RET + +; -------------- +; GO SUB Routine +; -------------- +; The present value of PPC and the incremented value of SUBPPC are stored on the GO SUB stack. + +L1A53: POP DE ; Save the return address. + LD H,(IY+#0D) ; SUBPPC. Fetch the statement number and increment it. + INC H ; + EX (SP),HL ; Exchange the 'error address' with the statement number. + INC SP ; Reclaim the use of a location. + + LD BC,(#5C45) ; PPC. + PUSH BC ; Next save the present line number. + PUSH HL ; Return the 'error address' to the machine stack + LD (#5C3D),SP ; ERR-SP. and reset ERR-SP to point to it. + PUSH DE ; Stack the return address. + + RST #28 ; + DEFW GO_TO ; #1E67. Now set NEWPPC & NSPPC to the required values. + + LD BC,#0014 ; But before making the jump make a test for room. + RST #28 ; + DEFW TEST_ROOM ; #1F05. Will automatically produce error '4' if out of memory. + RET + +; -------------- +; RETURN Routine +; -------------- +; The line number and the statement number that are to be made the object of a 'return' +; are fetched from the GO SUB stack. + +L1A6F: POP BC ; Fetch the return address. + POP HL ; Fetch the 'error address'. + POP DE ; Fetch the last entry on the GO SUB stack. + LD A,D ; The entry is tested to see if + CP #3E ; it is the GO SUB stack end marker. + JR Z,L1A86 ; Jump if it is. + + DEC SP ; The full entry uses three locations only. + EX (SP),HL ; Exchange the statement number with the 'error address'. + EX DE,HL ; Move the statement number. + LD (#5C3D),SP ; ERR_SP. Reset the error pointer. + PUSH BC ; Replace the return address. + LD (#5C42),HL ; NEWPPC. Enter the line number. + LD (IY+#0A),D ; NSPPC. Enter the statement number. + RET ; + +L1A86: PUSH DE ; Replace the end marker and + PUSH HL ; the 'error address'. + + CALL L05AC ; Produce error report. + DEFB #06 ; "7 RETURN without GO SUB" + +; -------------- +; DEF FN Routine +; -------------- +; During syntax checking a DEF FN statement is checked to ensure that it has the correct form. +; Space is also made available for the result of evaluating the function. +; But in 'run-time' a DEF FN statement is passed-by. + +L1A8C: BIT 7,(IY+#01) + JR Z,L1A97 ; Jump forward if checking syntax. + + LD A,#CE ; Otherwise bass-by the + JP L19FE ; 'DEF FN' statement. + +;First consider the variable of the function. + +L1A97: SET 6,(IY+#01) ; Signal 'a numeric variable'. + RST #28 ; + DEFW ALPHA ; #2C8D. Check that the present code is a letter. + JR NC,L1AB6 ; Jump forward if not. + + RST #20 ; Fetch the next character. + CP '$' ; #24. + JR NZ,L1AAA ; Jump forward unless it is a '$'. + + RES 6,(IY+#01) ; Change bit 6 as it is a string variable. + RST #20 ; Fetch the next character. + +L1AAA: CP '(' ; #28. A '(' must follow the variable's name. + JR NZ,L1AEA ; Jump forward if not. + + RST #20 ; Fetch the next character + CP ')' ; #29. Jump forward if it is a ')' + JR Z,L1AD3 ; as there are no parameters of the function. + +;A loop is now entered to deal with each parameter in turn. + +L1AB3: RST #28 ; + DEFW ALPHA ; #2C8D. + +L1AB6: JP NC,L1912 ; The present code must be a letter. + + EX DE,HL ; Save the pointer in DE. + RST #20 ; Fetch the next character. + CP '$' ; #24. + JR NZ,L1AC1 ; Jump forward unless it is a '$'. + + EX DE,HL ; Otherwise save the new pointer in DE instead. + RST #20 ; Fetch the next character. + +L1AC1: EX DE,HL ; Move the pointer to the last character of the name to HL. + LD BC,#0006 ; Now make six locations after that last character. + RST #28 ; + DEFW MAKE_ROOM ; #1655. + INC HL ; + INC HL ; + LD (HL),#0E ; Enter a 'number marker' into the first of the new locations. + CP ',' ; #2C. If the present character is a ',' then jump back as + JR NZ,L1AD3 ; there should be a further parameter. + + RST #20 ; + JR L1AB3 ; Otherwise jump out of the loop. + +;Next the definition of the function is considered. + +L1AD3: CP ')' ; #29. Check that the ')' does exist. + JR NZ,L1AEA ; Jump if not. + + RST #20 ; The next character is fetched. + CP '=' ; #3D. It must be an '='. + JR NZ,L1AEA ; Jump if not. + + RST #20 ; Fetch the next character. + LD A,(#5C3B) ; FLAGS. + PUSH AF ; Save the nature (numeric or string) of the variable + RST #28 ; + DEFW SCANNING ; #24FB. Now consider the definition as an expression. + POP AF ; Fetch the nature of the variable. + + XOR (IY+#01) ; FLAGS. Check that it is of the same type + AND #40 ; as found for the definition. + +L1AEA: JP NZ,L1912 ; Give an error report if required. + + CALL L18A1 ; Move on to consider the next statement in the line. + +; ------------ +; MOVE Routine +; ------------ + +L1AF0: RET ; Simply return. + + +; ====================== +; MENU ROUTINES - PART 1 +; ====================== + +;[v]..................................................................................................................... SP2000 [v] +; --------------- +; Run Tape Loader +; --------------- +; Used by Main Menu - Tape Loader option. +; L1AF1: LD HL,#EC0E ; Fetch mode. +; LD (HL),#FF ; Set Tape Loader mode. +; CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + +; RST #28 ; +; DEFW SET_MIN ; #16B0. Clear out editing area. + +; LD HL,(#5C59) ; E_LINE. +; LD BC,#0003 ; Create 3 bytes of space for the LOAD "" command. +; RST #28 ; +; DEFW MAKE_ROOM ; #1655. + +; LD HL,L1B6E ; Address of command bytes for LOAD "". +; LD DE,(#5C59) ; E_LINE. +; LD BC,#0003 ; +; LDIR ; Copy LOAD "" into the line editing area. + +; NEW PART FOR adr=1AF1h +L1AF1: +TRDOS: CALL CLS_X + CALL PROG1 + LD DE,TST1 + LD BC,LEN1 +L1AFD: +COM_LN: PUSH DE + PUSH BC + CALL_R1 SET_MIN + LD HL,(E_LINE) + POP BC + PUSH BC + PUSH HL + CALL_R1 MAKE_ROOM + POP DE + POP BC + POP HL + LDIR + + ;nop +;[^]..................................................................................................................... SP2000 [^] + CALL L026B ; Parse and execute the BASIC line. + ; [Will not return here but will exit via the error handler routine] +; + BLOCK #1B14-$,0 + +; ----------------------- +; List Program to Printer +; ----------------------- +; Used by Edit Menu - Print option. + +L1B14: CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + RST #28 ; + DEFW SET_MIN ; #16B0. Clear out editing area. + + LD HL,(#5C59) ; E_LINE. + LD BC,#0001 ; Create 1 byte of space. + RST #28 ; + DEFW MAKE_ROOM ; #1655. + + LD HL,(#5C59) ; E_LINE. + LD (HL),#E1 ; Copy LLIST into the line editing area. + + CALL L026B ; Parse and execute the BASIC line. + ; [Will not return here but will exit via the error handler routine] + + +; ======================================================= +; BASIC LINE AND COMMAND INTERPRETATION ROUTINES - PART 2 +; ======================================================= + +; ---------------- +; SPECTRUM Routine +; ---------------- +; Return to 48K BASIC Mode. This routine will force caps lock is off. + +L1B2B: CALL L1B53 ; Overwrite 'P' channel data to use the ZX Printer. + + LD SP,(#5C3D) ; ERR_SP. Purge the stack. + POP HL ; Remove error handler address. + + LD HL,MAIN_4 ; #1303. The main execution loop within ROM 1. + PUSH HL ; + + LD HL,PRINT_A_1+#0003 ; #0013. Address of a #FF byte within ROM 1, used to generate error report "0 OK". + PUSH HL ; + LD HL,ERROR_1 ; #0008. The address of the error handler within ROM 1. + PUSH HL ; + +; [*BUG* - Although the channel 'P' information has been reconfigured to use the ZX Printer, the ZX printer buffer and +; associated system variables still need to be cleared. Failure to do so means that the first use of the ZX Printer will +; cause garbage to the printed, i.e. the paging routines and new system variables still present in the ZX Printer buffer. +; Subsequently printer output will then be ok since the ZX Printer buffer and system variables will be cleared. +; Worse still, there is the possibility that new data to be printed will be inserted beyond the ZX Printer buffer since +; ROM 1 does not trap whether the ZX Printer system variable PR_POSN and PR_CC hold invalid values. The bug can be fixed +; by inserting the following instructions, which cause the ZX Printer buffer to be cleared immediately after switching to +; ROM 1 and before the error report "0 OK" is produced. Credit: Paul Farrow and Andrew Owen.] +; +; LD HL,CLEAR_PRB ; Address of the routine in ROM 1 to clear the ZX Printer buffer and associated system variables. +; PUSH HL ; +; SET 1,(IY+#01) ; FLAGS. Signal the printer is in use.] + + LD A,#20 ; Force 48K mode. + LD (BANK_M),A ; #5B5C. + JP SWAP_ROM ; #5B00. Swap to ROM 1 and return via a RST #08 / DEFB #FF. + + +; ====================== +; MENU ROUTINES - PART 2 +; ====================== + +; --------------------------- +; Main Menu - 48 BASIC Option +; --------------------------- + +L1B47: LD HL,#0000 ; Stack a #0000 address to return to. + PUSH HL ; + + LD A,#20 ; Force 48 mode. + LD (BANK_M),A ; #5B5C + JP SWAP_ROM ; #5B00. Swap to ROM 1, return to #0000. + +; -------------------- +; Set 'P' Channel Data +; -------------------- +; This routine overwrites the 'P' channel data with the 'S' channel data, i.e. the default values when using the ZX Printer. + +L1B53: LD HL,(#5C4F) ; CHANS. + LD DE,#0005 ; + ADD HL,DE ; HL=Address 'S' channel data. + LD DE,#000A ; + EX DE,HL ; HL=#000A, DE=Address 'S' channel data. + ADD HL,DE ; HL=Address 'P' channel data. + EX DE,HL ; DE=Address 'P' channel data, HL=Address 'S' channel data. + LD BC,#0004 ; + LDIR ; Copy the 'S' channel data over the 'P' channel data. + RES 3,(IY+#30) ; FLAGS2. Signal caps lock unset. [Not really necessary for switching back to 48 BASIC mode] + RES 4,(IY+#01) ; FLAGS. Signal not 128K mode. + RET ; + +; --------------------- +; LOAD "" Command Bytes +; --------------------- +; Used by the Tape Loader routine. + +L1B6E: DEFB #EF, #22, #22 ; LOAD "" + + +; ======================================================= +; BASIC LINE AND COMMAND INTERPRETATION ROUTINES - PART 3 +; ======================================================= + +; ------------- +; LLIST Routine +; ------------- + +L1B71: LD A,#03 ; Printer channel. + JR L1B77 ; Jump ahead to join LIST. + +; ------------ +; LIST Routine +; ------------ + +L1B75: LD A,#02 ; Main screen channel. + +L1B77: LD (IY+#02),#00 ; TV_FLAG. Signal 'an ordinary listing in the main part of the screen'. + RST #28 ; + DEFW SYNTAX_Z ; #2530. + JR Z,L1B83 ; Do not open the channel if checking syntax. + + RST #28 ; + DEFW CHAN_OPEN ; #1601. Open the channel. + +L1B83: RST #28 ; + DEFW GET_CHAR ; #0018. [Could just do RST #18] + RST #28 ; + DEFW STR_ALTER ; #2070. See if the stream is to be changed. + JR C,L1BA3 ; Jump forward if unchanged. + + RST #28 + DEFW GET_CHAR ; #0018. Get current character. + CP #3B ; Is it a ';'? + JR Z,L1B96 ; Jump if it is. + + CP ',' ; #2C. Is it a ','? + JR NZ,L1B9E ; Jump if it is not. + +L1B96: RST #28 ; + DEFW NEXT_CHAR ; #0020. Get the next character. + CALL L190E ; Indirectly call EXPT-1NUM in ROM 1 to check that + ; a numeric expression follows, e.g. LIST #5,20. + JR L1BA6 ; Jump forward with it. + +L1B9E: RST #28 ; + DEFW USE_ZERO ; #1CE6. Otherwise use zero and + JR L1BA6 ; jump forward. + +;Come here if the stream was unaltered. + +L1BA3: RST #28 ; + DEFW FETCH_NUM ; #1CDE. Fetch any line or use zero if none supplied. + +L1BA6: CALL L18A1 ; If checking the syntax of the edit-line move on to the next statement. + RST #28 ; + DEFW LIST_5+3 ; #1825. Delegate handling to ROM 1. + RET + +; ---------------------- +; RAM Disk SAVE! Routine +; ---------------------- + +L1BAD: LD (OLDSP),SP ; #5B81. Save SP. + LD SP,TSTACK ; #5BFF. Use temporary stack. + + CALL L1C97 ; Create new catalogue entry. + + LD BC,(HD_0B) ; #5B72. get the length of the file. + LD HL,#FFF7 ; -9 (9 is the length of the file header). + OR #FF ; Extend the negative number into the high byte. + SBC HL,BC ; AHL=-(length of file + 9). + CALL L1CF3 ; Check for space in RAM disk (produce "4 Out of memory" if no room). + + LD BC,#0009 ; File header length. + LD HL,HD_00 ; #5B71. Address of file header. + CALL L1DAC ; Store file header to RAM disk. + + LD HL,(HD_0D) ; #5B74. Start address of file data. + LD BC,(HD_0B) ; #5B72. Length of file data. + CALL L1DAC ; Store bytes to RAM disk. + CALL L1D56 ; Update catalogue entry (leaves logical RAM bank 4 paged in). + + LD A,#05 ; Page in logical RAM bank 5 (physical RAM bank 0). + CALL L1C64 ; + + LD SP,(OLDSP) ; #5B81. Use original stack. + RET ; + +; ------------ +; CAT! Routine +; ------------ + +L1BE5: RST #28 ; Get the current character. + DEFW GET_CHAR ; #0018. [Could just do RST #18 here] + CP '!' ; #21. Is it '!'? + JP NZ,L1912 ; Jump to "C Nonsense in BASIC" if not. + + RST #28 ; Get the next character. + DEFW NEXT_CHAR ; #0020. [Could just do RST #20 here] + CALL L18A1 ; Check for end of statement. + + LD A,#02 ; Select main screen. + RST #28 ; + DEFW CHAN_OPEN ; #1601. + + LD (OLDSP),SP ; #5B81. Store SP. + LD SP,TSTACK ; #5BFF. Use temporary stack. + + CALL L20D2 ; Print out the catalogue. + + LD A,#05 ; Page in logical RAM bank 5 (physical RAM bank 0). + CALL L1C64 ; + + LD SP,(OLDSP) ; #5B81. Use original stack. + RET ; + +; -------------- +; ERASE! Routine +; -------------- + +L1C0C: RST #28 ; Get character from BASIC line. + DEFW GET_CHAR ; #0018. + CP '!' ; #21. Is it '!'? + JP NZ,L1912 ; Jump to "C Nonsense in BASIC" if not. + + CALL L1393 ; Get the filename into N_STR1. + CALL L18A1 ; Make sure we've reached the end of the BASIC statement. + + LD (OLDSP),SP ; #5B81. Store SP. + LD SP,TSTACK ; #5BFF. Use temporary stack. + + CALL L1F5F ; Do the actual erasing (leaves logical RAM bank 4 paged in). + + LD A,#05 ; Restore RAM configuration. + CALL L1C64 ; Page in logical RAM bank 5 (physical RAM bank 0). + + LD SP,(OLDSP) ; #5B81. Use original stack. + RET ; + + +; ================================== +; RAM DISK COMMAND ROUTINES - PART 2 +; ================================== + +; ------------------------- +; Load Header from RAM Disk +; ------------------------- + +L1C2E: LD (OLDSP),SP ; #5B81. Store SP. + LD SP,TSTACK ; #5BFF. Use temporary stack. + + CALL L1D35 ; Find file (return details pointed to by IX). Leaves logical RAM bank 4 paged in. + +;The file exists else the call above would have produced an error "h file does not exist" + + LD HL,HD_00 ; #5B71. Load 9 header bytes. + LD BC,#0009 ; + CALL L1E37 ; Load bytes from RAM disk. + + LD A,#05 ; Restore RAM configuration. + CALL L1C64 ; Page in logical RAM bank 5 (physical RAM bank 0). + + LD SP,(OLDSP) ; #5B81. Use original stack. + RET ; + +; ------------------ +; Load from RAM Disk +; ------------------ +; Used by LOAD, VERIFY and MERGE. Note that VERIFY will simply perform a LOAD. +; Entry: HL=Destination address. +; DE=Length (will be greater than zero). +; IX=File descriptor. +; IX=Address of catalogue entry (IX+#10-IX+#12 points to the address of the file's data, past its header). +; HD_00-HD_11 holds file header information. + +L1C4B: LD (OLDSP),SP ; #5B81. Store SP + LD SP,TSTACK ; #5BFF. Use temporary stack. + + LD B,D ; + LD C,E ; BC=Length. + CALL L1E37 ; Load bytes from RAM disk. + CALL L1D56 ; Update catalogue entry (leaves logical RAM bank 4 paged in). + + LD A,#05 ; Restore RAM configuration. + CALL L1C64 ; Page in logical RAM bank 5 (physical RAM bank 0). + + LD SP,(OLDSP) ; #5B81. Use original stack. + RET ; + + +; ======================== +; PAGING ROUTINES - PART 1 +; ======================== + +; --------------------- +; Page Logical RAM Bank +; --------------------- +; This routine converts between logical and physical RAM banks and pages the +; selected bank in. +; Entry: A=Logical RAM bank. + +L1C64: PUSH HL ; Save BC and HL. + PUSH BC ; + + LD HL,L1C81 ; Physical banks used by RAM disk. + LD B,#00 ; + LD C,A ; BC=Logical RAM bank. + ADD HL,BC ; Point to table entry. + LD C,(HL) ; Look up physical page. + + DI ; Disable interrupts whilst paging. + LD A,(BANK_M) ; #5B5C. Fetch the current configuration. + AND #F8 ; Mask off current RAM bank. + OR C ; Include new RAM bank. + LD (BANK_M),A ; #5B5C. Store the new configuration. + LD BC,#7FFD ; + OUT (C),A ; Perform the page. + EI ; Re-enable interrupts. + + POP BC ; Restore BC and HL. + POP HL ; + RET ; + +; ------------------------------- +; Physical RAM Bank Mapping Table +; ------------------------------- + +L1C81: DEFB #01 ; Logical bank #00. + DEFB #03 ; Logical bank #01. + DEFB #04 ; Logical bank #02. + DEFB #06 ; Logical bank #03. + DEFB #07 ; Logical bank #04. + DEFB #00 ; Logical bank #05. + + +; ================================== +; RAM DISK COMMAND ROUTINES - PART 3 +; ================================== + +; ----------------- +; Compare Filenames +; ----------------- +; Compare filenames at N_STR1 and IX. +; Exit: Zero flag set if filenames match. +; Carry flag set if filename at DE is alphabetically lower than filename at IX. + +L1C87: LD DE,N_STR1 ; #5B67. + +; Compare filenames at DE and IX + +L1C8A: PUSH IX ; + POP HL ; + LD B,#0A ; Maximum of 10 characters. + +L1C8F: LD A,(DE) ; + INC DE ; + CP (HL) ; compare each character. + INC HL ; + RET NZ ; Return if characters are different. + + DJNZ L1C8F ; Repeat for all characters of the filename. + + RET ; + +; -------------------------- +; Create New Catalogue Entry +; -------------------------- +; Add a catalogue entry with filename contained in N_STR1. +; Exit: HL=Address of next free catalogue entry. +; IX=Address of newly created catalogue entry. + +L1C97: CALL L1D12 ; Find entry in RAM disk area, returning IX pointing to catalogue entry (leaves logical RAM bank 4 paged in). + JR Z,L1CA0 ; Jump ahead if does not exist. + + CALL L05AC ; Produce error report. + DEFB #20 ; "e File already exists" + +L1CA0: PUSH IX ; + LD BC,#3FEC ; 16384-20 (maximum size of RAM disk catalogue). + ADD IX,BC ; IX grows downwards as new RAM disk catalogue entries added. + ; If adding the maximum size to IX does not result in the carry flag being set + ; then the catalogue is full, so issue an error report "4 Out of Memory". + POP IX ; + JR NC,L1D0E ; Jump if out of memory. + + LD HL,#FFEC ; -20 (20 bytes is the size of a RAM disk catalogue entry). + LD A,#FF ; Extend the negative number into the high byte. + CALL L1CF3 ; Ensure space in RAM disk area. + LD HL,FLAGS3 ; #5B66. + SET 2,(HL) ; Signal editing RAM disk catalogue. + PUSH IX ; + POP DE ; DE=Address of new catalogue entry. + LD HL,N_STR1 ; #5B67. Filename. + +L1CBE: LD BC,#000A ; 10 characters in the filename. + LDIR ; Copy the filename. + + SET 0,(IX+#13) ; Indicate catalogue entry requires updating. + + LD A,(IX+#0A) ; Set the file access address to be the + LD (IX+#10),A ; start address of the file. + LD A,(IX+#0B) ; + LD (IX+#11),A ; + LD A,(IX+#0C) ; + LD (IX+#12),A ; + + XOR A ; Set the fill length to zero. + LD (IX+#0D),A ; + LD (IX+#0E),A ; + LD (IX+#0F),A ; + + LD A,#05 ; + CALL L1C64 ; Logical RAM bank 5 (physical RAM bank 0). + + PUSH IX ; + POP HL ; HL=Address of new catalogue entry. + LD BC,#FFEC ; -20 (20 bytes is the size of a catalogue entry). + ADD HL,BC ; + LD (SFNEXT),HL ; #5B83. Store address of next free catalogue entry. + RET ; + +; -------------------------- +; Adjust RAM Disk Free Space +; -------------------------- +; Adjust the count of free bytes within the RAM disk. +; The routine can produce "4 Out of memory" when adding. +; Entry: AHL=Size adjustment (negative when a file added, positive when a file deleted). +; A=Bit 7 set for adding data, else deleting data. + +L1CF3: LD DE,(SFSPACE) ; #5B85. + EX AF,AF' ; A'HL=Requested space. + + LD A,(SFSPACE+2) ; #5B87. ADE=Free space on RAM disk. + LD C,A ; CDE=Free space. + + EX AF,AF' ; AHL=Requested space. + BIT 7,A ; A negative adjustment, i.e. adding data? + JR NZ,L1D0A ; Jump ahead if so. + +;Deleting data + + ADD HL,DE ; + ADC A,C ; AHL=Free space left. + +L1D03: LD (SFSPACE),HL ; #5B85. Store free space. + LD (SFSPACE+2),A ; #5B87. + RET ; + +;Adding data + +L1D0A: ADD HL,DE ; + ADC A,C ; + JR C,L1D03 ; Jump back to store free space if space left. + +L1D0E: CALL L05AC ; Produce error report. + DEFB 03 ; "4 Out of memory" + +; --------------------------------- +; Find Catalogue Entry for Filename +; --------------------------------- +; Entry: Filename stored at N_STR1 (#5B67). +; Exit : Zero flag set if file does not exist. +; If file exists, IX points to catalogue entry. +; Always leaves logical RAM bank 4 paged in. + +L1D12: LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + LD IX,#EBEC ; Point to first catalogue entry. + +L1D1B: LD DE,(SFNEXT) ; #5B83. Pointer to last catalogue entry. + OR A ; Clear carry flag. + PUSH IX ; + POP HL ; HL=First catalogue entry. + SBC HL,DE ; + RET Z ; Return with zero flag set if end of catalogue reached + ; and hence filename not found. + + CALL L1C87 ; Test filename match with N_STR1 (#5B67). + JR NZ,L1D2E ; Jump ahead if names did not match. + + OR #FF ; Reset zero flag to indicate filename exists. + RET ; + +L1D2E: LD BC,#FFEC ; -20 bytes (20 bytes is the size of a catalogue entry). + ADD IX,BC ; Point to the next directory entry. + JR L1D1B ; Test the next name. + +; ------------------ +; Find RAM Disk File +; ------------------ +; Find a file in the RAM disk matching name held in N_STR1, +; and return with IX pointing to the catalogue entry. + +L1D35: CALL L1D12 ; Find entry in RAM disk area, returning IX pointing to catalogue entry (leaves logical RAM bank 4 paged in). + JR NZ,L1D3E ; Jump ahead if it exists. + + CALL L05AC ; Produce error report. + DEFB #23 ; "h File does not exist" + +L1D3E: LD A,(IX+#0A) ; Take the current start address (bank + location) + LD (IX+#10),A ; and store it as the current working address. + LD A,(IX+#0B) ; + LD (IX+#11),A ; + LD A,(IX+#0C) ; + LD (IX+#12),A ; + + LD A,#05 ; Page in logical RAM bank 5 (physical RAM bank 0). + CALL L1C64 ; + RET ; [Could have saved 1 byte by using JP #1C64 (ROM 0)] + +; ---------------------- +; Update Catalogue Entry +; ---------------------- +; Entry: IX=Address of catalogue entry (IX+#10-IX+#12 points to end of the file). +; Exits with logical RAM bank 4 paged in. + +L1D56: LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + BIT 0,(IX+#13) ; + RET Z ; Ignore if catalogue entry does not require updating. + + RES 0,(IX+#13) ; Indicate catalogue entry updated. + + LD HL,FLAGS3 ; #5B66. + RES 2,(HL) ; Signal not editing RAM disk catalogue. + + LD L,(IX+#10) ; Points to end address within logical RAM bank. + LD H,(IX+#11) ; + LD A,(IX+#12) ; Points to end logical RAM bank. + + LD E,(IX+#0A) ; Start address within logical RAM bank. + LD D,(IX+#0B) ; + LD B,(IX+#0C) ; Start logical RAM bank. + OR A ; Clear carry flag. + SBC HL,DE ; HL=End address-Start address. Maximum difference fits within 14 bits. + + SBC A,B ; A=End logical RAM bank-Start logical RAM bank - 1 if addresses overlap. + RL H ; + RL H ; Work out how many full banks of 16K are being used. + SRA A ; Place this in the upper two bits of H. + RR H ; + SRA A ; + RR H ; HL=Total length. + + LD (IX+#0D),L ; Length within logical RAM bank. + LD (IX+#0E),H ; + LD (IX+#0F),A ; + +;Copy the end address of the previous entry into the new entry + + LD L,(IX+#10) ; End address within logical RAM bank. + LD H,(IX+#11) ; + LD A,(IX+#12) ; End logical RAM bank. + LD BC,#FFEC ; -20 bytes (20 bytes is the size of a catalogue entry). + ADD IX,BC ; Address of next catalogue entry. + LD (IX+#0A),L ; Start address within logical RAM bank. + LD (IX+#0B),H ; + LD (IX+#0C),A ; Start logical RAM bank. + RET ; + +; ---------------------- +; Save Bytes to RAM Disk +; ---------------------- +; Entry: IX=Address of catalogue entry. +; HL=Source address in conventional RAM. +; BC=Length. +; Advances IX+#10-IX+#12 as bytes are saved so that always points to next location to fill, +; eventually pointing to the end of the file. + +L1DAC: LD A,B ; Check whether a data length of zero was requested. + OR C ; + RET Z ; Ignore if so since all bytes already saved. + + PUSH HL ; Save the source address. + LD DE,#C000 ; DE=The start of the upper RAM bank. + EX DE,HL ; HL=The start of the RAM bank. DE=Source address. + SBC HL,DE ; HL=RAM bank start - Source address. + JR Z,L1DD5 ; Jump ahead if saving bytes from #C000. + + JR C,L1DD5 ; Jump ahead if saving bytes from an address above #C000. + +;Source is below #C000 + + PUSH HL ; HL=Distance below #C000 (RAM bank start - Source address). + SBC HL,BC ; + JR NC,L1DCC ; Jump if requested bytes are all below #C000. + +;Source spans across #C000 + + LD H,B ; + LD L,C ; HL=Requested length. + POP BC ; BC=Distance below #C000. + OR A ; + SBC HL,BC ; HL=Bytes occupying upper RAM bank. + EX (SP),HL ; Stack it. HL=Source address. + LD DE,#C000 ; Start of upper RAM bank. + PUSH DE ; + JR L1DF4 ; Jump forward. + +;Source fits completely below upper RAM bank (less than #C000) + +L1DCC: POP HL ; Forget the 'distance below #C000' count. + POP HL ; HL=Source address. + LD DE,#0000 ; Remaining bytes to transfer. + PUSH DE ; + PUSH DE ; Stack dummy Start of upper RAM bank. + JR L1DF4 ; Jump forward. + +;Source fits completely within upper RAM bank (greater than or equal #C000) + +L1DD5: LD H,B ; + LD L,C ; HL=Requested length. + LD DE,#0020 ; DE=Length of buffer. + OR A ; + SBC HL,DE ; HL=Requested length-Length of buffer = Buffer overspill. + JR C,L1DE4 ; Jump if requested length will fit within the buffer. + +;Source spans transfer buffer + + EX (SP),HL ; Stack buffer overspill. HL=#0000. + LD B,D ; + LD C,E ; BC=Buffer length. + JR L1DE9 ; Jump forward. + +;Source fits completely within transfer buffer + +L1DE4: POP HL ; HL=Destination address. + LD DE,#0000 ; Remaining bytes to transfer. + PUSH DE ; Stack 'transfer buffer in use' flag. + +;Transfer a block + +L1DE9: PUSH BC ; Stack the length. + LD DE,STRIP1 ; #5B98. Transfer buffer. + LDIR ; Transfer bytes. + POP BC ; BC=Length. + PUSH HL ; HL=New source address. + LD HL,STRIP1 ; #5B98. Transfer buffer. + +L1DF4: LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + LD E,(IX+#10) ; + LD D,(IX+#11) ; Fetch the address from the current logical RAM bank. + LD A,(IX+#12) ; Logical RAM bank. + CALL L1C64 ; Page in appropriate logical RAM bank. + +L1E05: LDI ; Transfer a byte from the file to the required RAM disk location or transfer buffer. + LD A,D ; + OR E ; Has DE been incremented to #0000? + JR Z,L1E24 ; Jump if end of RAM bank reached. + +L1E0B: LD A,B ; + OR C ; + JP NZ,L1E05 ; Repeat until all bytes transferred. + + LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + LD (IX+#10),E ; + LD (IX+#11),D ; Store the next RAM bank source address. + + LD A,#05 ; Page in logical RAM bank 5 (physical RAM bank 0). + CALL L1C64 ; + + POP HL ; HL=Source address. + POP BC ; BC=Length. + JR L1DAC ; Re-enter this routine to transfer another block. + +;The end of a RAM bank has been reached so switch to the next bank + +L1E24: LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + INC (IX+#12) ; Increment to the new logical RAM bank. + LD A,(IX+#12) ; Fetch the new logical RAM bank. + LD DE,#C000 ; The start of the RAM disk + CALL L1C64 ; Page in next RAM bank. + JR L1E0B ; Jump back to transfer another block. + +; ------------------------ +; Load Bytes from RAM Disk +; ------------------------ +; Used for loading file header and data. +; Entry: IX=RAM disk catalogue entry address. IX+#10-IX+#12 points to the next address to fetch from the file. +; HL=Destination address. +; BC=Requested length. + +L1E37: LD A,B ; Check whether a data length of zero was requested. + OR C ; + RET Z ; Ignore if so since all bytes already loaded. + + PUSH HL ; Save the destination address. + LD DE,#C000 ; DE=The start of the upper RAM bank. + EX DE,HL ; HL=The start of the RAM bank. DE=Destination address. + SBC HL,DE ; HL=RAM bank start - Destination address. + JR Z,L1E67 ; Jump if destination is #C000. + JR C,L1E67 ; Jump if destination is above #C000. + +;Destination is below #C000 + +L1E45: PUSH HL ; HL=Distance below #C000 (RAM bank start - Destination address). + SBC HL,BC ; + JR NC,L1E5C ; Jump if requested bytes all fit below #C000. + +;Code will span across #C000 + + LD H,B ; + LD L,C ; HL=Requested length. + POP BC ; BC=Distance below #C000. + OR A ; + SBC HL,BC ; HL=Bytes destined for upper RAM bank. + EX (SP),HL ; Stack it. HL=Destination address. + LD DE,#0000 ; Remaining bytes to transfer. + PUSH DE ; + LD DE,#C000 ; Start of upper RAM bank. + PUSH DE ; + EX DE,HL ; HL=Start of upper RAM bank. + JR L1E80 ; Jump forward. + +;Code fits completely below upper RAM bank (less than #C000) + +L1E5C: POP HL ; Forget the 'distance below #C000' count. + POP HL ; HL=Destination address. + LD DE,#0000 ; Remaining bytes to transfer. + PUSH DE ; + PUSH DE ; Stack dummy Start of upper RAM bank. + PUSH DE ; + EX DE,HL ; HL=#0000, DE=Destination address. + JR L1E80 ; Jump forward. + +;Code destined for upper RAM bank (greater than or equal to #C000) + +L1E67: LD H,B ; + LD L,C ; HL=Requested length. + LD DE,#0020 ; DE=Length of buffer. + OR A ; + SBC HL,DE ; HL=Requested length-Length of buffer = Buffer overspill. + JR C,L1E76 ; Jump if requested length will fit within the buffer. + +;Code will span transfer buffer + + EX (SP),HL ; Stack buffer overspill. HL=#0000. + LD B,D ; + LD C,E ; BC=Buffer length. + JR L1E7B ; Jump forward. + +;Code will all fit within transfer buffer + +L1E76: POP HL ; HL=Destination address. + LD DE,#0000 ; Remaining bytes to transfer. + PUSH DE ; Stack 'transfer buffer in use' flag. + +L1E7B: PUSH BC ; Stack the length. + PUSH HL ; Stack destination address. + LD DE,STRIP1 ; #5B98. Transfer buffer. + +;Transfer a block + +L1E80: LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + LD L,(IX+#10) ; RAM bank address. + LD H,(IX+#11) ; + LD A,(IX+#12) ; Logical RAM bank. + CALL L1C64 ; Page in appropriate logical RAM bank. + +;Enter a loop to transfer BC bytes, either to required destination or to the transfer buffer + +L1E91: LDI ; Transfer a byte from the file to the required location or transfer buffer. + LD A,H ; + OR L ; Has HL been incremented to #0000? + JR Z,L1EBC ; Jump if end of RAM bank reached. + +L1E97: LD A,B ; + OR C ; + JP NZ,L1E91 ; Repeat until all bytes transferred. + + LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + LD (IX+#10),L ; + LD (IX+#11),H ; Store the next RAM bank destination address. + + LD A,#05 ; Page in logical RAM bank 5 (physical RAM bank 0). + CALL L1C64 ; + + POP DE ; DE=Destination address. + POP BC ; BC=Length. + + LD HL,STRIP1 ; #5B98. Transfer buffer. + LD A,B ; + OR C ; All bytes transferred? + JR Z,L1EB7 ; Jump forward if so. + + LDIR ; Transfer code in buffer to the required address. + +L1EB7: EX DE,HL ; HL=New destination address. + POP BC ; BC=Remaining bytes to transfer. + JP L1E37 ; Re-enter this routine to transfer another block. + +;The end of a RAM bank has been reached so switch to the next bank + +L1EBC: LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + INC (IX+#12) ; Increment to the new logical RAM bank. + LD A,(IX+#12) ; Fetch the new logical RAM bank. + LD HL,#C000 ; The start of the RAM disk. + CALL L1C64 ; Page in next logical RAM bank. + JR L1E97 ; Jump back to transfer another block. + +; ------------------------------------------------- +; Transfer Bytes to RAM Bank 4 - Vector Table Entry +; ------------------------------------------------- +; This routine can be used to transfer bytes from the current RAM bank into logical RAM bank 4. +; It is not used in this ROM and is a remnant of the original Spanish Spectrum 128 ROM 0. +; Entry: HL=Source address in conventional RAM. +; DE=Destination address in logical RAM bank 4 (physical RAM bank 7). +; BC=Number of bytes to save. + +L1ECF: PUSH AF ; Save AF. + + LD A,(BANK_M) ; #5B5C. Fetch current physical RAM bank configuration. + PUSH AF ; Save it. + PUSH HL ; Save source address. + PUSH DE ; Save destination address. + PUSH BC ; Save length. + + LD IX,N_STR1+3 ; #5B6A. + + LD (IX+#10),E ; Store destination address as the current address pointer. + LD (IX+#11),D ; + LD (IX+#12),#04 ; Destination is in logical RAM bank 4 (physical RAM bank 7). + + CALL L1DAC ; Store bytes to RAM disk. + +;Entered here by load vector routine + +L1EE8: LD A,#05 ; Page in logical RAM bank 5 (physical RAM bank 0). + CALL L1C64 ; + + POP BC ; Get length. + POP DE ; Get destination address. + POP HL ; Get source address. + + ADD HL,BC ; HL=Address after end of source. + EX DE,HL ; DE=Address after end of source. HL=Destination address. + ADD HL,BC ; HL=Address after end of destination. + EX DE,HL ; HL=Address after end of source. DE=Address after end of destination. + + POP AF ; Get original RAM bank configuration. + LD BC,#7FFD ; + DI ; Disable interrupts whilst paging. + OUT (C),A ; + LD (BANK_M),A ; #5B5C. + EI ; Re-enable interrupts. + + LD BC,#0000 ; Signal all bytes loaded/saved. + POP AF ; Restore AF. + RET ; + +; --------------------------------------------------- +; Transfer Bytes from RAM Bank 4 - Vector Table Entry +; --------------------------------------------------- +; This routine can be used to transfer bytes from logical RAM bank 4 into the current RAM bank. +; It is not used in this ROM and is a remnant of the original Spanish Spectrum 128 ROM 0. +; Entry: HL=Source address in logical RAM bank 4 (physical RAM bank 7). +; DE=Destination address in current RAM bank. +; BC=Number of bytes to load. + +L1F04: PUSH AF ; Save AF. + + LD A,(BANK_M) ; #5B5C. Fetch current physical RAM bank configuration. + PUSH AF ; Save it. + PUSH HL ; Save source address. + PUSH DE ; Save destination address. + PUSH BC ; Save length. + + LD IX,N_STR1+3 ; #5B6A. + + LD (IX+#10),L ; Store source address as the current address pointer. + LD (IX+#11),H ; + LD (IX+#12),#04 ; Source is in logical RAM bank 4 (physical RAM bank 7). + + EX DE,HL ; HL=Destination address. + CALL L1E37 ; Load bytes from RAM disk. + JR L1EE8 ; Join the save vector routine above. + + +; ======================== +; PAGING ROUTINES - PART 2 +; ======================== + +; ---------------------------- +; Use Normal RAM Configuration +; ---------------------------- +; Page in physical RAM bank 0, use normal stack and stack TARGET address. +; Entry: HL=TARGET address. +RET_SP: +L1F20: EX AF,AF' ; Save AF. + + LD A,#00 ; Physical RAM bank 0. + DI ; Disable interrupts whilst paging. + CALL L1F3A ; Page in physical RAM bank 0. + POP AF ; AF=Address on stack when CALLed. + LD (TARGET),HL ; #5B58. Store HL. + LD HL,(OLDSP) ; #5B81. Fetch the old stack. + LD (OLDSP),SP ; #5B81. Save the current stack. + LD SP,HL ; Use the old stack. + EI ; Re-enable interrupts. + LD HL,(TARGET) ; #5B58. Restore HL. + PUSH AF ; Re-stack the return address. + + EX AF,AF' ; Get AF back. + RET ; + +; --------------- +; Select RAM Bank +; --------------- +; Used twice by the ROM to select either physical RAM bank 0 or physical RAM bank 7. +; However, it could in theory also be used to set other paging settings. +; Entry: A=RAM bank number. + +L1F3A: PUSH BC ; Save BC + LD BC,#7FFD ; + OUT (C),A ; Perform requested paging. + LD (BANK_M),A ; #5B5C. + POP BC ; Restore BC. + RET ; + +; ------------------------------- +; Use Workspace RAM Configuration +; ------------------------------- +; Page in physical RAM bank 7, use workspace stack and stack TARGET address. +; Entry: HL=TARGET address. + +L1F45: EX AF,AF' ; Save A. + + DI ; Disable interrupts whilst paging. + + POP AF ; Fetch return address. + LD (TARGET),HL ; #5B58. Store HL. + + LD HL,(OLDSP) ; #5B81. Fetch the old stack. + LD (OLDSP),SP ; #5B81. Save the current stack. + LD SP,HL ; Use the old stack. + + LD HL,(TARGET) ; #5B58. Restore HL. + PUSH AF ; Stack return address. + + LD A,#07 ; RAM bank 7. + CALL L1F3A ; Page in RAM bank 7. + EI ; Re-enable interrupts. + + EX AF,AF' ; Restore A. + RET ; + + +; ================================== +; RAM DISK COMMAND ROUTINES - PART 4 +; ================================== + +; --------------------- +; Erase a RAM Disk File +; --------------------- +; N_STR1 contains the name of the file to erase. + +L1F5F: CALL L1D12 ; Find entry in RAM disk area, returning IX pointing to catalogue entry (leaves logical RAM bank 4 paged in). + JR NZ,L1F68 ; Jump ahead if it was found. [Could have saved 3 bytes by using JP Z,#1D3E (ROM 0)] + + CALL L05AC ; Produce error report. + DEFB #23 ; "h File does not exist" + +L1F68: LD L,(IX+#0D) ; AHL=Length of file. + LD H,(IX+#0E) ; + LD A,(IX+#0F) ; Bit 7 of A will be 0 indicating to delete rather than add. + CALL L1CF3 ; Free up this amount of space. + + PUSH IY ; Preserve current value of IY. + + LD IY,(SFNEXT) ; #5B83. IY points to next free catalogue entry. + LD BC,#FFEC ; BC=-20 (20 bytes is the size of a catalogue entry). + ADD IX,BC ; IX points to the next catalogue entry + + LD L,(IY+#0A) ; AHL=First spare byte in RAM disk file area. + LD H,(IY+#0B) ; + LD A,(IY+#0C) ; + + POP IY ; Restore IY to normal value. + + LD E,(IX+#0A) ; BDE=Start of address of next RAM disk file entry. + LD D,(IX+#0B) ; + LD B,(IX+#0C) ; + OR A ; + SBC HL,DE ; + SBC A,B ; + RL H ; + RL H ; + SRA A ; + RR H ; + SRA A ; + RR H ; HL=Length of all files to be moved. + + LD BC,#0014 ; 20 bytes is the size of a catalogue entry. + ADD IX,BC ; IX=Catalogue entry to delete. + + LD (IX+#10),L ; Store file length in the 'deleted' catalogue entry. + LD (IX+#11),H ; + LD (IX+#12),A ; + + LD BC,#FFEC ; -20 (20 bytes is the size of a catalogue entry). + ADD IX,BC ; IX=Next catalogue entry. + + LD L,(IX+#0A) ; DHL=Start address of next RAM disk file entry. + LD H,(IX+#0B) ; + LD D,(IX+#0C) ; + + LD BC,#0014 ; 20 bytes is the size of a catalogue entry. + ADD IX,BC ; IX points to catalogue entry to delete. + + LD A,D ; Page in logical RAM bank for start address of entry to delete. + CALL L1C64 ; + + LD A,(BANK_M) ; #5B5C. + LD E,A ; Save current RAM bank configuration in E. + LD BC,#7FFD ; Select physical RAM bank 7. + LD A,#07 ; + DI ; Disable interrupts whilst performing paging operations. + OUT (C),A ; Page in selected RAM bank. + EXX ; DHL'=Start address of next RAM disk file entry. + + LD L,(IX+#0A) ; DHL=Start of address of RAM disk file entry to delete. + LD H,(IX+#0B) ; + LD D,(IX+#0C) ; + + LD A,D ; + CALL L1C64 ; Page in logical RAM bank for file entry (will update BANK_M). + + LD A,(BANK_M) ; #5B5C. + LD E,A ; Get RAM bank configuration for the file in E. + LD BC,#7FFD ; + EXX ; DHL=Start address of next RAM disk file entry. + +; At this point we have the registers and alternate registers pointing +; to the actual bytes in the RAM disk for the file to be deleted and the next file, +; with length bytes of the catalogue entry for the file to be deleted containing +; the length of bytes for all subsequent files that need to be moved down in memory. +; A loop is entered to move all of these bytes where the delete file began. + +; DHL holds the address of the byte to be moved. +; E contains the value which should be OUTed to #5B5C to page in the relevant RAM page. + +L1FEA: LD A,#07 ; Select physical RAM bank 7. + DI ; Disable interrupts whilst performing paging operations. + OUT (C),A ; Page in selected RAM bank. + + LD A,(IX+#10) ; Decrement end address. + SUB #01 ; + LD (IX+#10),A ; + JR NC,L200D ; If no carry then the decrement is finished. + + LD A,(IX+#11) ; Otherwise decrement the middle byte. + SUB #01 ; + LD (IX+#11),A ; + JR NC,L200D ; If no carry then the decrement is finished. + + LD A,(IX+#12) ; Otherwise decrement the highest byte. + SUB #01 ; + LD (IX+#12),A ; + JR C,L203E ; Jump forward if finished moving the file. + +L200D: OUT (C),E ; Page in RAM bank containing the next file. + LD A,(HL) ; Get the byte from the next file. + INC L ; Increment DHL. + JR NZ,L2024 ; If not zero then the increment is finished. + + INC H ; Otherwise increment the middle byte. + JR NZ,L2024 ; If not zero then the increment is finished. + + EX AF,AF' ; Save the byte read from the next file. + INC D ; Advance to next logical RAM bank for the next file. + + LD A,D ; + CALL L1C64 ; Page in next logical RAM bank for next file entry (will update BANK_M). + + LD A,(BANK_M) ; #5B5C. + LD E,A ; Get RAM bank configuration for the next file in E. + LD HL,#C000 ; The next file continues at the beginning of the next RAM bank. + EX AF,AF' ; Retrieve the byte read from the next file. + +L2024: EXX ; DHL=Address of file being deleted. + + DI ; Disable interrupts whilst performing paging operations. + OUT (C),E ; Page in next RAM bank containing the next file. + + LD (HL),A ; Store the byte taken from the next file. + INC L ; Increment DHL. + JR NZ,L203B ; If not zero then the increment is finished. + + INC H ; Otherwise increment the middle byte. + JR NZ,L203B ; If not zero then the increment is finished. + + INC D ; Advance to next logical RAM bank for the file being deleted. + + LD A,D ; + CALL L1C64 ; Page in next logical RAM bank for file being deleted entry (will update BANK_M). + + LD A,(BANK_M) ; #5B5C. + LD E,A ; Get RAM bank configuration for the file being deleted in E. + LD HL,#C000 ; The file being deleted continues at the beginning of the next RAM bank. + +L203B: EXX ; DHL=Address of byte in next file. + ; DHL'=Address of byte in file being deleted. + JR L1FEA ; + +;The file has been moved + +L203E: LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + LD A,#00 ; + LD HL,#0014 ; AHL=20 bytes is the size of a catalogue entry. + +L2048: CALL L1CF3 ; Delete a catalogue entry. + + LD E,(IX+#0D) ; + LD D,(IX+#0E) ; + LD C,(IX+#0F) ; CDE=File length of file entry to delete. + + LD A,D ; + RLCA ; + RL C ; + RLCA ; + RL C ; C=RAM bank. + LD A,D ; + AND #3F ; Mask off upper bits to leave length in this bank (range 0-16383). + LD D,A ; DE=Length in this bank. + + PUSH IX ; Save address of catalogue entry to delete. + +L2061: PUSH DE ; + LD DE,#FFEC ; -20 (20 bytes is the size of a catalogue entry). + ADD IX,DE ; Point to next catalogue entry. + POP DE ; DE=Length in this bank. + + LD L,(IX+#0A) ; + LD H,(IX+#0B) ; + LD A,(IX+#0C) ; AHL=File start address. + OR A ; + SBC HL,DE ; Will move into next RAM bank? + SUB C ; + BIT 6,H ; + JR NZ,L207C ; Jump if same RAM bank. + + SET 6,H ; New address in next RAM bank. + DEC A ; Next RAM bank. + +L207C: LD (IX+#0A),L ; + LD (IX+#0B),H ; + LD (IX+#0C),A ; Save new start address of file. + + LD L,(IX+#10) ; + LD H,(IX+#11) ; + LD A,(IX+#12) ; Fetch end address of file. + OR A ; + SBC HL,DE ; Will move into next RAM bank? + SUB C ; + BIT 6,H ; + JR NZ,L2099 ; Jump if same RAM bank. + + SET 6,H ; New address in next RAM bank. + DEC A ; Next RAM bank. + +L2099: LD (IX+#10),L ; + LD (IX+#11),H ; + LD (IX+#12),A ; Save new end address of file. + + PUSH IX ; + POP HL ; HL=Address of next catalogue entry. + + PUSH DE ; + LD DE,(SFNEXT) ; #5B83. + OR A ; + SBC HL,DE ; End of catalogue reached? + POP DE ; DE=Length in this bank. + JR NZ,L2061 ; Jump if not to move next entry. + + LD DE,(SFNEXT) ; #5B83. Start address of the next available catalogue entry. + + POP HL ; + PUSH HL ; HL=Start address of catalogue entry to delete. + + OR A ; + SBC HL,DE ; + LD B,H ; + LD C,L ; BC=Length of catalogue entries to move. + POP HL ; + PUSH HL ; HL=Start address of catalogue entry to delete. + LD DE,#0014 ; 20 bytes is the size of a catalogue entry. + ADD HL,DE ; HL=Start address of previous catalogue entry. + EX DE,HL ; DE=Start address of previous catalogue entry. + POP HL ; HL=Start address of catalogue entry to delete. + DEC DE ; DE=End address of catalogue entry to delete. + DEC HL ; HL=End address of next catalogue entry. + LDDR ; Move all catalogue entries. + + LD HL,(SFNEXT) ; #5B83. Start address of the next available catalogue entry. + LD DE,#0014 ; 20 bytes is the size of a catalogue entry. + ADD HL,DE ; + LD (SFNEXT),HL ; #5B83. Store the new location of the next available catalogue entry. + RET ; + +; ------------------------ +; Print RAM Disk Catalogue +; ------------------------ +; This routine prints catalogue filenames in alphabetically order. +; It does this by repeatedly looping through the catalogue to find +; the next 'highest' name. + +L20D2: LD A,#04 ; Page in logical RAM bank 4 + CALL L1C64 ; (physical RAM bank 7) + + LD HL,L2121 ; HL points to ten #00 bytes, the initial comparison filename. + +L20DA: LD BC,L212B ; BC point to ten #FF bytes. + LD IX,#EBEC ; IX points to first catalogue entry. + +L20E1: CALL L05D6 ; Check for BREAK. + + PUSH IX ; Save address of catalogue entry. + + EX (SP),HL ; HL points to current catalogue entry. Top of stack points to ten #00 data. + LD DE,(SFNEXT) ; #5B83. Find address of next free catalogue entry. + OR A ; + SBC HL,DE ; Have we reached end of catalogue? + + POP HL ; Fetch address of catalogue entry. + JR Z,L2111 ; Jump ahead if end of catalogue reached. + + LD D,H ; + LD E,L ; DE=Current catalogue entry. + PUSH HL ; + PUSH BC ; + CALL L1C8A ; Compare current filename (initially ten #00 bytes). + POP BC ; + POP HL ; + JR NC,L210A ; Jump if current catalogue name is 'above' the previous. + + LD D,B ; + LD E,C ; DE=Last filename + PUSH HL ; + PUSH BC ; + CALL L1C8A ; Compare current filename (initially ten #FF bytes). + POP BC ; + POP HL ; + JR C,L210A ; Jump if current catalogue name is 'below' the previous. + + PUSH IX ; + POP BC ; BC=Address of current catalogue entry name. + +L210A: LD DE,#FFEC ; -20 (20 bytes is the size of a catalogue entry). + ADD IX,DE ; Point to next catalogue entry. + JR L20E1 ; Check next filename. + +L2111: PUSH HL ; HL points to current catalogue entry. + LD HL,L212B ; Address of highest theoretical filename data. + OR A ; + SBC HL,BC ; Was a new filename to print found? + POP HL ; + RET Z ; Return if all filenames printed. + + LD H,B ; + LD L,C ; HL=Address of current catalogue entry name. + CALL L2135 ; Print the catalogue entry. + JR L20DA ; Repeat for next filename. + +; ----------------------------- +; Print Catalogue Filename Data +; ----------------------------- + +L2121: DEFB #00, #00, #00, #00, #00 ; Lowest theoretical filename. + DEFB #00, #00, #00, #00, #00 + +L212B: DEFB #FF, #FF, #FF, #FF, #FF ; Highest theoretical filename. + DEFB #FF, #FF, #FF, #FF, #FF + +; ---------------------------- +; Print Single Catalogue Entry +; ---------------------------- +; Entry: HL=Address of filename. +; BC=Address of filename. + +L2135: PUSH HL ; Save address of filename. + + PUSH BC ; + POP HL ; [No need to transfer BC to HL since they already have the same value]. + + LD DE,N_STR1 ; #5B67. Copy the filename to N_STR1 so that it + LD BC,#000A ; is visible when this RAM bank is paged out. + LDIR ; + + LD A,#05 ; Page in logical RAM bank 5 (physical RAM bank 0). + CALL L1C64 ; + + LD HL,(OLDSP) ; #5B81. + LD (OLDSP),SP ; #5B81. Save temporary stack. + LD SP,HL ; Use original stack. + + LD HL,N_STR1 ; #5B67. HL points to filename. + LD B,#0A ; 10 characters to print. + +L2152: LD A,(HL) ; Print each character of the filename. + PUSH HL ; + PUSH BC ; + RST #28 ; + DEFW PRINT_A_1 ; #0010. + POP BC ; + POP HL ; + INC HL ; + DJNZ L2152 ; + + LD A,#0D ; Print a newline character. + RST #28 ; + DEFW PRINT_A_1 ; #0010. + + RST #28 ; + DEFW TEMPS ; #0D4D. Copy permanent colours to temporary colours. + + LD HL,(OLDSP) ; #5B81. + LD (OLDSP),SP ; #5B81. Save original stack. + LD SP,HL ; Switch back to temporary stack. + + LD A,#04 ; Page in logical RAM bank 4 (physical RAM bank 7). + CALL L1C64 ; + + POP HL ; HL=Address of filename. + RET ; + + +; ======================================================= +; BASIC LINE AND COMMAND INTERPRETATION ROUTINES - PART 4 +; ======================================================= + +; -------------- +; LPRINT Routine +; -------------- + +L2174: LD A,#03 ; Printer channel. + JR L217A ; Jump ahead. + +; -------------- +; PRINT Routine +; -------------- + +L2178: LD A,#02 ; Main screen channel. + +L217A: RST #28 ; + DEFW SYNTAX_Z ; #2530. + JR Z,L2182 ; Jump forward if syntax is being checked. + + RST #28 ; + DEFW CHAN_OPEN ; #1601. + +L2182: RST #28 ; + DEFW TEMPS ; #0D4D. + RST #28 ; + DEFW PRINT_2 ; #1FDF. Delegate handling to ROM 1. + CALL L18A1 ; "C Nonsense in BASIC" during syntax checking if not + ; at end of line or statement. + RET ; + +; ------------- +; INPUT Routine +; ------------- +; This routine allows for values entered from the keyboard to be assigned +; to variables. It is also possible to have print items embedded in the +; INPUT statement and these items are printed in the lower part of the display. + +L218C: RST #28 ; + DEFW SYNTAX_Z ; #2530. + JR Z,L2199 ; Jump forward if syntax is being checked. + + LD A,#01 ; Open channel 'K'. + RST #28 ; + DEFW CHAN_OPEN ; #1601. + RST #28 ; Clear the lower part of the display. + DEFW CLS_LOWER ; #0D6E. [*BUG* - This call will re-select channel 'S' and so should have been called prior to opening + ; channel 'K'. It is a direct copy of the code that appears in the standard Spectrum ROM (and ROM 1). It is + ; debatable whether it is better to reproduce the bug so as to ensure that the INPUT routine operates the same + ; in 128K mode as it does in 48K mode. Credit: Geoff Wearmouth] + +L2199: LD (IY+#02),#01 ; TV_FLAG. Signal that the lower screen is being handled. [Not a bug as has been reported elsewhere. The confusion seems to have + ; arisen due to the incorrect system variable being originally mentioned in the Spectrum ROM Disassembly by Logan and O'Hara] + RST #28 ; + DEFW IN_ITEM_1 ; #20C1. Call the subroutine to deal with the INPUT items. + CALL L18A1 ; Move on to the next statement if checking syntax. + RST #28 ; + DEFW INPUT_1+#000A ; #20A0. Delegate handling to ROM 1. + RET ; + +; ------------ +; COPY Routine +; ------------ + +L21A7: JP L08F0 ; Jump to new COPY routine. + +; ----------- +; NEW Routine +; ----------- + +L21AA: DI ; + JP L019D ; Re-initialise the machine. + +; -------------- +; CIRCLE Routine +; -------------- +; This routine draws an approximation to the circle with centre co-ordinates +; X and Y and radius Z. These numbers are rounded to the nearest integer before use. +; Thus Z must be less than 87.5, even when (X,Y) is in the centre of the screen. +; The method used is to draw a series of arcs approximated by straight lines. + +L21AE: RST #18 ; Get character from BASIC line. + CP ',' ; #2C. Check for second parameter. + JR NZ,L21EB ; Jump ahead (for error C) if not. + + RST #20 ; Advance pointer into BASIC line. + RST #28 ; Get parameter. + DEFW EXPT_1NUM ; #1C82. Radius to calculator stack. + CALL L18A1 ; Move to consider next statement if checking syntax. + RST #28 ; + DEFW CIRCLE+#000D ; #232D. Delegate handling to ROM 1. + RET ; + +; ------------ +; DRAW Routine +; ------------ +; This routine is entered with the co-ordinates of a point X0, Y0, say, in +; COORDS. If only two parameters X, Y are given with the DRAW command, it +; draws an approximation to a straight line from the point X0, Y0 to X0+X, Y0+Y. +; If a third parameter G is given, it draws an approximation to a circular arc +; from X0, Y0 to X0+X, Y0+Y turning anti-clockwise through an angle G radians. + +L21BE: RST #18 ; Get current character. + CP ',' ; #2C. + JR Z,L21CA ; Jump if there is a third parameter. + + CALL L18A1 ; Error C during syntax checking if not at end of line/statement. + RST #28 ; + DEFW LINE_DRAW ; #2477. Delegate handling to ROM 1. + RET ; + +L21CA: RST #20 ; Get the next character. + RST #28 ; + DEFW EXPT_1NUM ; #1C82. Angle to calculator stack. + CALL L18A1 ; Error C during syntax checking if not at end of line/statement. + RST #28 ; + DEFW DR_3_PRMS+#0007 ; #2394. Delegate handling to ROM 1. + RET ; + +; ----------- +; DIM Routine +; ----------- +; This routine establishes new arrays in the variables area. The routine starts +; by searching the existing variables area to determine whether there is an existing +; array with the same name. If such an array is found then it is 'reclaimed' before +; the new array is established. A new array will have all its elements set to zero +; if it is a numeric array, or to 'spaces' if it is an array of strings. + +L21D5: RST #28 ; Search to see if the array already exists. + DEFW LOOK_VARS ; #28B2. + JR NZ,L21EB ; Jump if array variable not found. + + RST #28 + DEFW SYNTAX_Z ; #2530. + JR NZ,L21E7 ; Jump ahead during syntax checking. + + RES 6,C ; Test the syntax for string arrays as if they were numeric. + RST #28 ; + DEFW STK_VAR ; #2996. Check the syntax of the parenthesised expression. + CALL L18A1 ; Error when checking syntax unless at end of line/statement. + +;An 'existing array' is reclaimed. + +L21E7: RST #28 ; + DEFW D_RUN ; #2C15. Delegate handling to ROM 1. + RET ; + +; ---------------------------------- +; Error Report C - Nonsense in BASIC +; ---------------------------------- + +L21EB: CALL L05AC ; Produce error report. + DEFB #0B ; "C Nonsense in BASIC" + +; -------------------- +; Clear Screen Routine +; -------------------- +; Clear screen if it is not already clear. + +L21EF: BIT 0,(IY+#30) ; FLAGS2. Is the screen clear? + RET Z ; Return if it is. + + RST #28 ; + DEFW CL_ALL ; #0DAF. Otherwise clear the whole display. + RET ; + +; --------------------------- +; Evaluate Numeric Expression +; --------------------------- +; This routine is called when a numerical expression is typed directly into the editor or calculator. +; A numeric expression is any that begins with '(', '-' or '+', or is one of the function keywords, e.g. ABS, SIN, etc, +; or is the name of a numeric variable. + +L21F8: LD HL,#FFFE ; A line in the editing area is considered as line '-2'. + LD (#5C45),HL ; PPC. Signal no current line number. + +;Check the syntax of the BASIC line + + RES 7,(IY+#01) ; Indicate 'syntax checking' mode. + CALL L228E ; Point to start of the BASIC command line. + + RST #28 ; + DEFW SCANNING ; #24FB. Evaluate the command line. + BIT 6,(IY+#01) ; Is it a numeric value? + JR Z,L223A ; Jump to produce an error if a string result. + + RST #18 ; Get current character. + CP #0D ; Is it the end of the line? + JR NZ,L223A ; Jump if not to produce an error if not. + +;The BASIC line has passed syntax checking so now execute it + + SET 7,(IY+#01) ; If so, indicate 'execution' mode. + CALL L228E ; Point to start of the BASIC command line. + + LD HL,L0321 ; Set up the error handler routine address. + LD (SYNRET),HL ; #5B8B. + + RST #28 ; + DEFW SCANNING ; #24FB. Evaluate the command line. + BIT 6,(IY+#01) ; Is it a numeric value? + JR Z,L223A ; Jump to produce an error if a string result. + + LD DE,LASTV ; #5B8D. DE points to last calculator value. + LD HL,(#5C65) ; STKEND. + LD BC,#0005 ; The length of the floating point value. + OR A ; + SBC HL,BC ; HL points to value on top of calculator stack. + LDIR ; Copy the value in the workspace to the top of the calculator stack. + JP L223E ; [Could have saved 1 byte by using a JR instruction] + +L223A: CALL L05AC ; Produce error report. + DEFB #19 ; "Q Parameter error" + +L223E: LD A,#0D ; Make it appear that 'Enter' has been pressed. + CALL L226F ; Process key press. + + LD BC,#0001 ; + RST #28 ; + DEFW BC_SPACES ; #0030. Create a byte in the workspace. + + LD (#5C5B),HL ; K_CUR. Address of the cursor. + PUSH HL ; Save it. + + LD HL,(#5C51) ; CURCHL. Current channel information. + PUSH HL ; Save it. + + LD A,#FF ; Channel 'R', the workspace. + RST #28 ; + DEFW CHAN_OPEN ; #1601. + + RST 28H + DEFW PRINT_FP ; #2DE3. Print a floating point number to the workspace. + + POP HL ; Get the current channel information address. + + RST #28 ; + DEFW CHAN_FLAG ; #1615. Set appropriate flags back for the old channel. + + POP DE ; DE=Address of the old cursor position. + + LD HL,(#5C5B) ; K_CUR. Address of the cursor. + AND A ; + SBC HL,DE ; HL=Length of floating point number. + +L2264: LD A,(DE) ; Fetch the character and make it appear to have been typed. + CALL L226F ; Process the key press. + INC DE ; + DEC HL ; Decrement floating point number character count. + LD A,H ; + OR L ; + JR NZ,L2264 ; Repeat for all characters. + + RET ; + +; ----------------- +; Process Key Press +; ----------------- +; Entry: A=Key code. + +L226F: PUSH HL ; Save registers. + PUSH DE ; + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD HL,#EC0D ; Editor flags. + RES 3,(HL) ; Reset 'line altered' flag + + PUSH AF ; + LD A,#02 ; Main screen + RST #28 ; + DEFW CHAN_OPEN ; #1601. + POP AF ; + + CALL L2669 ; Process key press. + + LD HL,#EC0D ; Editor flags. + RES 3,(HL) ; Reset 'line altered' flag + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + POP DE ; Restore registers. + POP HL ; + RET ; + +; --------------------------- +; Find Start of BASIC Command +; --------------------------- +; Point to the start of a typed in BASIC command +; and return first character in A. + +L228E: LD HL,(#5C59) ; E_LINE. Get the address of command being typed in. + DEC HL ; + LD (#5C5D),HL ; CH_ADD. Store it as the address of next character to be interpreted. + RST #20 ; Get the next character. + RET ; + +; --------------- +; Is LET Command? +; --------------- +; A typed in command resides in the editing workspace. +; This function tests whether the text is a single LET command. +; Exit: Zero flag set if a single LET command. + +L2297: CALL L228E ; Point to start of typed in command. + CP #F1 ; Is it 'LET'? + RET NZ ; Return if not with zero flag reset. + + LD HL,(#5C5D) ; CH_ADD. HL points to next character. + +L22A0: LD A,(HL) ; Fetch next character. + INC HL ; + CP #0D ; Has end of line been found? + RET Z ; Return if so with zero flag set. + + CP ':' ; #3A. Has start of new statement been found? + JR NZ,L22A0 ; Loop back if not. + + OR A ; Return zero flag reset indicating a multi-statement + RET ; LET command. + +; ---------------------- +; Is Operator Character? +; ---------------------- +; Exit: Zero flag set if character is an operator. + +L22AB: LD B,A ; Save B. + LD HL,L22BD ; Start of operator token table. + +L22AF: LD A,(HL) ; Fetch character from the table. + INC HL ; Advance to next entry. + OR A ; End of table? + JR Z,L22B9 ; Jump if end of table reached. + + CP B ; Found required character? + JR NZ,L22AF ; Jump if not to try next character in table. + +;Found + + LD A,B ; Restore character to A. + RET ; Return with zero flag set to indicate an operator. + +;Not found + +L22B9: OR #FF ; Reset zero flag to indicate not an operator. + LD A,B ; Restore character to A. + RET ; + +; --------------------- +; Operator Tokens Table +; --------------------- + +L22BD: DEFB #2B, #2D, #2A ; '+', '-', '*' + DEFB #2F, #5E, #3D ; '/', '^', '=' + DEFB #3E, #3C, #C7 ; '>', '<', '<=' + DEFB #C8, #C9, #C5 ; '>=', '<>', 'OR' + DEFB #C6 ; 'AND' + DEFB #00 ; End marker. + +; ---------------------- +; Is Function Character? +; ---------------------- +; Exit: Zero set if a function token. + +L22CB: CP #A5 ; 'RND'. (first 48K token) + JR C,L22DD ; Jump ahead if not a token with zero flag reset. + + CP #C4 ; 'BIN'. + JR NC,L22DD ; Jump ahead if not a function token. + + CP #AC ; 'AT'. + JR Z,L22DD ; Jump ahead if not a function token. + + CP #AD ; 'TAB'. + JR Z,L22DD ; Jump ahead if not a function token. + + CP A ; Return zero flag set if a function token. + RET ; + +L22DD: CP #A5 ; Return zero flag set if a function token. + RET ; + +; ---------------------------------- +; Is Numeric or Function Expression? +; ---------------------------------- +; Exit: Zero flag set if a numeric or function expression. + +L22E0: LD B,A ; Fetch character code. + OR #20 ; Make lowercase. + CP 'a' ; #61. Is it 'a' or above? + JR C,L22ED ; Jump ahead if not a letter. + + CP '{' ; #7B. Is it below '{'? +L22E9: JR NC,L22ED ; Jump ahead if not. + + CP A ; Character is a letter so return + RET ; with zero flag set. + +L22ED: LD A,B ; Fetch character code. + CP '.' ; #2E. Is it '.'? + RET Z ; Return zero flag set indicating numeric. + + CALL L230A ; Is character a number? + JR NZ,L2307 ; Jump ahead if not a number. + +L22F6: RST #20 ; Get next character. + CALL L230A ; Is character a number? + JR Z,L22F6 ; Repeat for next character if numeric. + + CP '.' ; #2E. Is it '.'? + RET Z ; Return zero flag set indicating numeric. + + CP 'E' ; #45. Is it 'E'? + RET Z ; Return zero flag set indicating numeric. + + CP 'e' ; #65. Is it 'e'? + RET Z ; Return zero flag set indicating numeric. + + JR L22AB ; Jump to test for operator tokens. + +L2307: OR #FF ; Reset the zero flag to indicate non-alphanumeric. + RET ; + +; --------------------- +; Is Numeric Character? +; --------------------- +; Exit: Zero flag set if numeric character. + +L230A: CP '0' ; #30. Is it below '0'? + JR C,L2314 ; Jump below '0'. + + CP ':' ; #3A. Is it below ':'? + JR NC,L2314 ; Jump above '9' + + CP A ; + RET ; Set zero flag if numeric. + +L2314: CP '0' ; #30. This will cause zero flag to be reset. + RET ; + +; ------------ +; PLAY Routine +; ------------ + +L2317: LD B,#00 ; String index. + RST #18 ; + +L231A: PUSH BC ; + RST #28 ; Get string expression. + DEFW EXPT_EXP + POP BC ; + INC B ; + CP ',' ; #2C. A ',' indicates another string. + JR NZ,L2327 ; Jump ahead if no more. + + RST #20 ; Advance to the next character. + JR L231A ; Loop back. + +L2327: LD A,B ; Check the index. + CP #09 ; Maximum of 8 strings (to support synthesisers, drum machines or sequencers). + JR C,L2330 ; + + CALL L05AC ; Produce error report. + DEFB #2B ; "p (c) 1986 Sinclair Research Ltd" [*BUG* - This should be "Parameter error". The Spanish 128 + ; produces "p Bad parameter" but to save memory perhaps the UK 128 was intended to use the existing + ; "Q Parameter error" and the change of the error code byte here was overlooked. In that case it would + ; have had a value of #19. Note that generation of this error when using the main screen editor will + ; result in a crash. Credit: Andrew Owen] + +L2330: CALL L18A1 ; Ensure end-of-statement or end-of-line. + JP L0985 ; Continue with PLAY code. + + +; ======================== +; UNUSED ROUTINES - PART 1 +; ======================== + +; There now follows 513 bytes of routines that are not used by the ROM, from #2336 (ROM 0) to #2536 (ROM 0). +; They are remnants of the original Spanish 128's ROM code, although surprisingly they appear in a different +; order within that ROM. + +; ---------------- +; Return to Editor +; ---------------- +; [Never called by this ROM] + +L2336: LD HL,TSTACK ; #5BFF. + LD (OLDSP),HL ; #5B81. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + JP L25CB ; Jump ahead to the Editor. + +; ------------------------ +; BC=HL-DE, Swap HL and DE +; ------------------------ +; Exit: BC=HL-DE. +; DE=HL, HL=DE. +; +; [Never called by this ROM] + +L2342: AND A ; + SBC HL,DE ; + LD B,H ; + LD C,L ; BC=HL-DE. + ADD HL,DE ; + EX DE,HL ; HL=DE, DE=HL. + RET ; + +; ---------------------- +; Create Room for 1 Byte +; ---------------------- +; Creates a single byte in the workspace, or automatically produces an error '4' if not. +; +; [Never called by this ROM] + +L234A: LD BC,#0001 ; Request 1 byte. + PUSH HL ; + PUSH DE ; + CALL L2358 ; Test whether there is space. If it fails this will cause the error + POP DE ; handler in ROM 0 to be called. If MAKE_ROOM were called directly and + POP HL ; and out of memory condition detected then the ROM 1 error handler would + RST #28 ; be called instead. + DEFW MAKE_ROOM ; #1655. The memory check passed so safely make the room. + RET + +; ------------------ +; Room for BC Bytes? +; ------------------ +; Test whether there is room for the specified number of bytes in the spare memory, +; producing error "4 Out of memory" if not. This routine is very similar to that at +; #3F66 with the exception that this routine assumes IY points at the system variables. +; Entry: BC=Number of bytes required. +; Exit : Returns if the room requested is available else an error '4' is produced. +; +; [Called by the routine at #234A (ROM 0), which is itself never called by this ROM] + +L2358: LD HL,(#5C65) ; STKEND. + ADD HL,BC ; Would adding the specified number of bytes overflow the RAM area? + JR C,L2368 ; Jump to produce an error if so. + + EX DE,HL ; DE=New end address. + LD HL,#0082 ; Would there be at least 130 bytes at the top of RAM? + ADD HL,DE ; + JR C,L2368 ; Jump to produce an error if not. + + SBC HL,SP ; If the stack is lower in memory, would there still be enough room? + RET C ; Return if there would. + +L2368: LD (IY+#00),#03 ; Signal error "4 Out of Memory". + JP L0321 ; Jump to error handler routine. + +; --------- +; HL = A*32 +; --------- +; [Called by routines at #2383 (ROM 0) and #23B8 (ROM 0), which are themselves never called by this ROM] + +L236F: ADD A,A ; A*2. + ADD A,A ; A*4. Then multiply by 8 in following routine. + +; -------- +; HL = A*8 +; -------- +; [Called by the routine at #23E1 (ROM 0), which ultimately is itself never called by this ROM] + +L2371: LD L,A ; + LD H,#00 ; + ADD HL,HL ; A*2. + ADD HL,HL ; A*4. + ADD HL,HL ; A*8. + RET ; Return HL=A*8. + +; ------------------------- +; Find Amount of Free Space +; ------------------------- +; Exit: Carry flag set if no more space, else HL holds the amount of free space. +; +; [Never called by this ROM] + +L2378: LD HL,#0000 ; + ADD HL,SP ; HL=SP. + LD DE,(#5C65) ; STKEND. + OR A ; + SBC HL,DE ; Effectively SP-STKEND, i.e. the amount of available space. + RET ; + +; ----------------------- +; Print Screen Buffer Row +; ----------------------- +; Prints row from the screen buffer to the screen. +; Entry: A=Row number. +; +; [Never called by this ROM] + +L2384: RES 0,(IY-#39) ; KSTATE+1. Signal do not invert attribute value. [IY+#3B on the Spanish 128] + + CALL L236F ; HL=A*32. Number of bytes prior to the requested row. + PUSH HL ; Save offset to requested row to print. + + LD DE,(#FF24) ; Fetch address of screen buffer. + ADD HL,DE ; Point to row entry. + LD D,H ; + LD E,L ; DE=Address of row entry. + EX (SP),HL ; Stack address of row entry. HL=Offset to requested row to print. + + PUSH HL ; Save offset to requested row to print. + PUSH DE ; Save address of row entry. + LD DE,#5800 ; Attributes file. + ADD HL,DE ; Point to start of corresponding row in attributes file. + EX DE,HL ; DE=Start address of corresponding row in attributes file. + POP HL ; HL=Address of row entry. + + LD BC,#0020 ; 32 columns. + LD A,(#5C8F) ; ATTR_T. Fetch the temporary colours. + CALL L249B ; Set the colours for the 32 columns in this row, processing + ; any colour control codes from the print string. + + POP HL ; HL=Offset to requested row to print. + LD A,H ; + LD H,#00 ; Calculate corresponding display file address. + ADD A,A ; + ADD A,A ; + ADD A,A ; + ADD A,#40 ; + LD D,A ; + LD E,H ; + ADD HL,DE ; + EX DE,HL ; DE=Display file address. + + POP HL ; HL=Offset to requested row to print. + LD B,#20 ; 32 columns. + JP L23E1 ; Print one row to the display file. + +; --------------------------- +; Blank Screen Buffer Content +; --------------------------- +; Sets the specified number of screen buffer positions from the specified row to #FF. +; Entry: A=Row number. +; BC=Number of bytes to set. +; +; [Never called by this ROM] + +L23B8: LD D,#FF ; The character to set the screen buffer contents to. + CALL L236F ; HL=A*32. Offset to the specified row. + LD A,D ; + LD DE,(#FF24) ; Fetch the address of the screen buffer. + ADD HL,DE ; HL=Address of first column in the requested row. + LD E,L ; + LD D,H ; + INC DE ; DE=Address of second column in the requested row. + LD (HL),A ; Store the character. + DEC BC ; + LDIR ; Repeat for all remaining bytes required. + RET ; + +; ----------------------------------- +; Print Screen Buffer to Display File +; ----------------------------------- +; [Never called by this ROM] + +L23CB: CALL L2488 ; Set attributes file from screen buffer. + + LD DE,#4000 ; DE=First third of display file. + LD HL,(#FF24) ; Fetch address of screen buffer. + LD B,E ; Display 256 characters. + CALL L23E1 ; Display string. + + LD D,#48 ; Middle third of display file. + CALL L23E1 ; Display string. + + LD D,#50 ; Last third of display file. + LD B,#C0 ; Display 192 characters. + +; ---------------------------------------------- +; Print Screen Buffer Characters to Display File +; ---------------------------------------------- +; Displays ASCII characters, UDGs, graphic characters or two special symbols in the display file, +; but does not alter the attributes file. Character code #FE is used to represent the error marker +; bug symbol and the character code #FF is used to represent a null, which is displayed as a space. +; Entry: DE=Display file address. +; HL=Points to string to print. +; B=Number of characters to print. +; +; [Used by routine at #23CB (ROM 0) and called by the routine at #2383 (ROM 0), both of which are themselves never called by this ROM] + +L23E1: LD A,(HL) ; Fetch the character. + PUSH HL ; Save string pointer. + PUSH DE ; Save display file address. + CP #FE ; Was if #FE (bug) or #FF (null)? + JR C,L23EC ; Jump ahead if not. + + SUB #FE ; Reduce range to #00-#01. + JR L2422 ; Jump ahead to show symbol. + +;Comes here if character code if below #FE + +L23EC: CP #20 ; Is it a control character? + JR NC,L23F7 ; Jump ahead if not. + +;Comes here if a control character + + LD HL,L2527 ; Graphic for a 'G' (not a normal G though). Used to indicate embedded colour control codes. + AND A ; Clear the carry flag to indicate no need to switch back to RAM bank 7. + EX AF,AF' ; Save the flag. + JR L242B ; Jump ahead to display the symbol. + +L23F7: CP #80 ; Is it a graphic character or UDG? + JR NC,L2409 ; Jump ahead if so. + +;Comes here if an ASCII character + + CALL L2371 ; HL=A*8. + LD DE,(#5C36) ; CHARS. + ADD HL,DE ; Point to the character bit pattern. + POP DE ; Fetch the display file address. + CALL #FF28 ; Copy character into display file (via RAM Routine). + ; Can't use routine at #242C (ROM 0) since it does not perform a simple return. + JR L2450 ; Continue with next character. + +;Comes here if a graphic character or UDG + +L2409: CP #90 ; Is it a graphic character? + JR NC,L2411 ; Jump ahead if not. + +;Comes here if a graphic character + + SUB #7F ; Reduce range to #01-#10. + JR L2422 ; Jump ahead to display the symbol. + +;Comes here if a UDG + +L2411: SUB #90 ; Reduce range to #00-#6D. + CALL L2371 ; HL=A*8. + + POP DE ; Fetch display file address. + CALL L1F20 ; Use Normal RAM Configuration (RAM bank 0) to allow access to character bit patterns. + PUSH DE ; Save display file address. + + LD DE,(#5C7B) ; UDG. Fetch address of UDGs. + SCF ; Set carry flag to indicate need to switch back to RAM bank 7. + JR L2429 ; Jump ahead to locate character bit pattern and display the symbol. + +;Come here if (HL) was #FE or #FF, or with a graphic character. +;At this point A=#00 if (HL) was #FE indicating a bug symbol, or #01 if (HL) was #FF indicating a null, +;or A=#01-#10 if a graphic character. + +L2422: LD DE,L252F ; Start address of the graphic character bitmap table. + CALL L2371 ; HL=A*8 -> #0000 or #0008. + AND A ; Clear carry flag to indicate no need to switch back to RAM bank 7. + +L2429: EX AF,AF' ; Save switch bank indication flag. + ADD HL,DE ; Point to the symbol bit pattern data. + +L242B: POP DE ; Fetch display file address. Drop through into routine below. + +; ------------------------------------ +; Copy A Character <<< RAM Routine >>> +; ------------------------------------ +; Routine copied to RAM at #FF36-#FF55 by subroutine at #246F (ROM 0). +; Also used in ROM from above routine. +; +; This routine copies 8 bytes from HL to DE. It increments HL and D after +; each byte, restoring D afterwards. +; It is used to copy a character into the display file. +; Entry: HL=Character data. +; DE=Display file address. +; +; [Called by a routine that is itself never called by this ROM] + +L242C: LD C,D ; Save D. + + LD A,(HL) ; + LD (DE),A ; Copy byte 1. + + INC HL ; + INC D ; + LD A,(HL) ; + LD (DE),A ; Copy byte 2. + + INC HL ; + INC D ; + LD A,(HL) ; + LD (DE),A ; Copy byte 3. + + INC HL ; + INC D ; + LD A,(HL) ; + LD (DE),A ; Copy byte 4. + + INC HL ; + INC D ; + LD A,(HL) ; + LD (DE),A ; Copy byte 5. + + INC HL ; + INC D ; + LD A,(HL) ; + LD (DE),A ; Copy byte 6. + + INC HL ; + INC D ; + LD A,(HL) ; + LD (DE),A ; Copy byte 7. + + INC HL ; + INC D ; + LD A,(HL) ; + LD (DE),A ; Copy byte 8. + + LD D,C ; Restore D. <<< Last byte copied to RAM >>> + +; When the above routine is used in ROM, it drops through to here. + +L244C: EX AF,AF' ; Need to switch back to RAM bank 7? + CALL C,L1F45 ; If so then switch to use Workspace RAM configuration (physical RAM bank 7). + +L2450: POP HL ; Fetch address of string data. + INC HL ; Move to next character. + INC DE ; Advance to next display file column. + DJNZ L23E1 ; Repeat for all requested characters. + + RET ; + +; --------------------------------- +; Toggle ROMs 1 <<< RAM Routine >>> +; --------------------------------- +; Routine copied to RAM at #FF28-#FF35 by subroutine at #246F (ROM 0). +; +; This routine toggles to the other ROM than the one held in BANK_M. +; Entry: A'= Current paging configuration. +; +; [Called by a routine that is itself never called by this ROM] + +L2456: PUSH BC ; Save BC + + DI ; Disable interrupts whilst paging. + + LD BC,#7FFD ; + LD A,(BANK_M) ; #5B5C. Fetch current paging configuration. + XOR #10 ; Toggle ROMs. + OUT (C),A ; Perform paging. + EI ; Re-enable interrupts. + EX AF,AF' ; Save the new configuration in A'. <<< Last byte copied to RAM >>> + +; --------------------------------- +; Toggle ROMs 2 <<< RAM Routine >>> +; --------------------------------- +; Routine copied to RAM at #FF56-#FF60 by subroutine at #246F (ROM 0). +; +; This routine toggles to the other ROM than the one specified. +; It is used to page back to the original configuration. +; Entry: A'= Current paging configuration. +; +; [Called by a routine that is itself never called by this ROM] + +L2464: EX AF,AF' ; Retrieve current paging configuration. + DI ; Disable interrupts whilst paging. + LD C,#FD ; Restore Paging I/O port number. + XOR #10 ; Toggle ROMs. + OUT (C),A ; Perform paging. + EI ; Re-enable interrupts. + + POP BC ; Restore BC. + RET ; <<< Last byte copied to RAM >>> + +; ----------------------------------------- +; Construct 'Copy Character' Routine in RAM +; ----------------------------------------- +; This routine copies 3 sections of code into RAM to construct a single +; routine that can be used to copy the bit pattern for a character into +; the display file. +; +; Copy #2456-#2463 (ROM 0) to #FF28-#FF35 (14 bytes). +; Copy #242C-#244B (ROM 0) to #FF36-#FF55 (32 bytes). +; Copy #2464-#246E (ROM 0) to #FF56-#FF60 (11 bytes). +; +; [Never called by this ROM] + +L246F: LD HL,L2456 ; Point to the 'page in other ROM' routine. + LD DE,#FF28 ; Destination RAM address. + LD BC,#000E ; + LDIR ; Copy the routine. + + PUSH HL ; + LD HL,L242C ; Copy a character routine. + LD C,#20 ; + LDIR ; Copy the routine. + + POP HL ; HL=#2464 (ROM 0), which is the address of the 'page back to original ROM' routine. + LD C,#0B ; + LDIR ; Copy the routine. + RET ; + +; -------------------------------------- +; Set Attributes File from Screen Buffer +; -------------------------------------- +; This routine parses the screen buffer string contents looking for colour +; control codes and changing the attributes file contents correspondingly. +; +; [Called by the routine at #23CB (ROM 0), which is itself never called by this ROM] + +L2488: RES 0,(IY-#39) ; KSTATE+1. Signal do not invert attribute value. [Spanish 128 uses IY-#3B] + + LD DE,#5800 ; The start of the attributes file. + LD BC,#02C0 ; 22 rows of 32 columns. + LD HL,(#FF24) ; The address of the string to print. + LD A,(#5C8D) ; ATTR_P. + LD (#5C8F),A ; ATTR_T. Use the permanent colours. + +; -------------------------------------- +; Set Attributes for a Screen Buffer Row +; -------------------------------------- +; Entry: A=Colour byte. +; HL=Address within screen buffer. +; BC=Number of characters. +; DE=Address within attributes file. + +L249B: EX AF,AF' ; Save the colour byte. + +;The main loop returns here on each iteration + +L249C: PUSH BC ; Save the number of characters. + + LD A,(HL) ; Fetch a character from the buffer. + CP #FF ; Is it blank? + JR NZ,L24AA ; Jump ahead if not. + + LD A,(#5C8D) ; ATTR_P. Get the default colour byte. + LD (DE),A ; Store it in the attributes file. + INC HL ; Point to next screen buffer position. + INC DE ; Point to next attributes file position. + JR L2507 ; Jump ahead to handle the next character. + +;Not a blank character + +L24AA: EX AF,AF' ; Get the colour byte. + LD (DE),A ; Store it in the attributes file. + INC DE ; Point to the next attributes file position. + EX AF,AF' ; Save the colour byte. + INC HL ; Point to the next screen buffer position. + + CP #15 ; Is the string character OVER or above? + JR NC,L2507 ; Jump if it is to handle the next character. + + CP #10 ; Is the string character below INK? + JR C,L2507 ; Jump if it is to handle the next character. + +;Screen buffer character is INK, PAPER, FLASH, BRIGHT or INVERSE. + + DEC HL ; Point back to the previous screen buffer position. + JR NZ,L24C2 ; Jump if not INK. + +;Screen character was INK so insert the new ink into the attribute byte. + + INC HL ; Point to the next screen buffer position. + LD A,(HL) ; Fetch the ink colour from the next screen buffer position. + LD C,A ; and store it in C. + EX AF,AF' ; Get the colour byte. + AND #F8 ; Mask off the ink bits. + JR L2505 ; Jump ahead to store the new attribute value and then to handle the next character. + +L24C2: CP #11 ; Is the string character PAPER? + JR NZ,L24D1 ; Jump ahead if not. + +;Screen character was PAPER so insert the new paper into the attribute byte. + + INC HL ; Point to the next screen buffer position. + LD A,(HL) ; Fetch the paper colour from the next screen buffer position. + ADD A,A ; + ADD A,A ; + ADD A,A ; Multiple by 8 so that ink colour become paper colour. + LD C,A ; + EX AF,AF' ; Get the colour byte. + AND #C7 ; Mask off the paper bits. + JR L2505 ; Jump ahead to store the new attribute value and then to handle the next character. + +L24D1: CP #12 ; Is the string character FLASH? + JR NZ,L24DE ; Jump ahead if not. + +;Screen character was FLASH + + INC HL ; Point to the next screen buffer position. + LD A,(HL) ; Fetch the flash status from the next screen buffer position. + RRCA ; Shift the flash bit into bit 0. + LD C,A ; + EX AF,AF' ; Get the colour byte. + AND #7F ; Mask off the flash bit. + JR L2505 ; Jump ahead to store the new attribute value and then to handle the next character. + +L24DE: CP #13 ; Is the string character BRIGHT? + JR NZ,L24EC ; Jump ahead if not. + +;Screen character was BRIGHT + + INC HL ; Point to the next screen buffer position. + LD A,(HL) ; Fetch the bright status from the next screen buffer position. + RRCA ; + RRCA ; Shift the bright bit into bit 0. + LD C,A ; + EX AF,AF' ; Get the colour byte. + AND #BF ; Mask off the bright bit. + JR L2505 ; Jump ahead to store the new attribute value and then to handle the next character. + +L24EC: CP #14 ; Is the string character INVERSE? + INC HL ; Point to the next screen buffer position. + JR NZ,L2507 ; Jump ahead if not to handle the next character. + +;Screen character was INVERSE + + LD C,(HL) ; Fetch the inverse status from the next screen buffer position. + LD A,(#5C01) ; KSTATE+1. Fetch inverting status (Bit 0 is 0 for non-inverting, 1 for inverting). + XOR C ; Invert status. + RRA ; Shift status into the carry flag. + JR NC,L2507 ; Jump if not inverting to handle the next character. + + LD A,#01 ; Signal inverting is active. + XOR (IY-#39) ; KSTATE+1. Toggle the status. + LD (#5C01),A ; KSTATE+1. Store the new status. + EX AF,AF' ; Get the colour byte. + + CALL L2513 ; Swap ink and paper in the colour byte. + +L2505: OR C ; Combine the old and new colour values. + EX AF,AF' ; Save the new colour byte. + +L2507: POP BC ; Fetch the number of characters. + DEC BC ; + LD A,B ; + OR C ; + JP NZ,L249C ; Repeat for all characters. + + EX AF,AF' ; Get colour byte. + LD (#5C8F),A ; ATTR_T. Make it the new temporary colour. + RET ; + +; --------------------------------- +; Swap Ink and Paper Attribute Bits +; --------------------------------- +; Entry: A=Attribute byte value. +; Exit : A=Attribute byte value with paper and ink bits swapped. +; +; [Called by the routine at #2488 (ROM 0), which is itself never called by this ROM] + +L2513: LD B,A ; Save the original colour byte. + + AND #C0 ; Keep only the flash and bright bits. + LD C,A ; + LD A,B ; + ADD A,A ; Shift ink bits into paper bits. + ADD A,A ; + ADD A,A ; + AND #38 ; Keep only the paper bits. + OR C ; Combine with the flash and bright bits. + LD C,A ; + + LD A,B ; Get the original colour byte. + RRA ; + RRA ; + RRA ; Shift the paper bits into the ink bits. + AND #07 ; Keep only the ink bits. + OR C ; Add with the paper, flash and bright bits. + RET ; + +; -------------- +; Character Data +; -------------- + +;Graphic control code indicator + +L2527: DEFB #00 ; 0 0 0 0 0 0 0 0 + DEFB #3C ; 0 0 1 1 1 1 0 0 XXXX + DEFB #62 ; 0 1 1 0 0 0 1 0 XX X + DEFB #60 ; 0 1 1 0 0 0 0 0 XX + DEFB #6E ; 0 1 1 0 1 1 1 0 XX XXX + DEFB #62 ; 0 1 1 0 0 0 1 0 XX X + DEFB #3E ; 0 0 1 1 1 1 1 0 XXXX + DEFB #00 ; 0 0 0 0 0 0 0 0 + +;Error marker + +L252F: DEFB #00 ; 0 0 0 0 0 0 0 0 + DEFB #6C ; 0 1 1 0 1 1 0 0 XX XX + DEFB #10 ; 0 0 0 1 0 0 0 0 X + DEFB #54 ; 0 1 0 1 0 1 0 0 X X X + DEFB #BA ; 1 0 1 1 1 0 1 0 X XXX X + DEFB #38 ; 0 0 1 1 1 0 0 0 XXX + DEFB #54 ; 0 1 0 1 0 1 0 0 X X X + DEFB #82 ; 1 0 0 0 0 0 1 0 X X + +; <<< End of Unused ROM Routines >>> + + +; ================= +; KEY ACTION TABLES +; ================= + +; ------------------------- +; Editing Keys Action Table +; ------------------------- +; Each editing key code maps to the appropriate handling routine. +; This includes those keys which mirror the functionality of the +; add-on keypad; these are found by trapping the keyword produced +; by the keystrokes in 48K mode. +; [Surprisingly there is no attempt to produce an intelligible layout; +; instead the first 16 keywords have been used. Additionally the entries for DELETE +; and ENTER should probably come in the first six entries for efficiency reasons.] + +L2537: DEFB #15 ; Number of table entries. + DEFB #0B ; Key code: Cursor up. + DEFW L2A94 ; CURSOR-UP handler routine. + DEFB #0A ; Key code: Cursor Down. + DEFW L2AB5 ; CURSOR-DOWN handler routine. + DEFB #08 ; Key code: Cursor Left. + DEFW L2AD7 ; CURSOR-LEFT handler routine. + DEFB #09 ; Key code: Cursor Right. + DEFW L2AE3 ; CURSOR-RIGHT handler routine. + DEFB #AD ; Key code: Extend Mode + P. + DEFW L2A4F ; TEN-ROWS-UP handler routine. + DEFB #AC ; Key code: Symbol Shift + I. + DEFW L2A25 ; TEN-ROWS-DOWN handler routine. + DEFB #AF ; Key code: Extend Mode + I. + DEFW L29D4 ; WORD-LEFT handler routine. + DEFB #AE ; Key code: Extend Mode + Shift + J. + DEFW L29E1 ; WORD-RIGHT handler routine. + DEFB #A6 ; Key code: Extend Mode + N, or Graph + W. + DEFW L2983 ; TOP-OF-PROGRAM handler routine. + DEFB #A5 ; Key code: Extend Mode + T, or Graph + V. + DEFW L29AB ; END-OF-PROGRAM handler routine. + DEFB #A8 ; Key code: Extend Mode Symbol Shift + 2, or Graph Y. + DEFW L2A87 ; START-OF-LINE handler routine. + DEFB #A7 ; Key code: Extend Mode + M, or Graph + X. + DEFW L2A7A ; END-OF-LINE handler routine. + DEFB #AA ; Key code: Extend Mode + Shift + K. + DEFW L291B ; DELETE-RIGHT handler routine. + DEFB #0C ; Key code: Delete. + DEFW L292B ; DELETE handler routine. + DEFB #B3 ; Key code: Extend Mode + W. + DEFW L3017 ; DELETE-WORD-RIGHT handler routine. + DEFB #B4 ; Key code: Extend Mode + E. + DEFW L2FBC ; DELETE-WORD-LEFT handler routine. + DEFB #B0 ; Key code: Extend Mode + J. + DEFW L3072 ; DELETE-TO-END-OF-LINE handler routine. + DEFB #B1 ; Key code: Extend Mode + K. + DEFW L303E ; DELETE-TO-START-OF-LINE handler routine. + DEFB #0D ; Key code: Enter. + DEFW L2944 ; ENTER handler routine. + DEFB #A9 ; Key code: Extend Mode + Symbol Shift + 8, or Graph + Z. + DEFW L269B ; TOGGLE handler routine. + DEFB #07 ; Key code: Edit. + DEFW L2704 ; MENU handler routine. + +; ---------------------- +; Menu Keys Action Table +; ---------------------- +; Each menu key code maps to the appropriate handling routine. + +L2577: DEFB #04 ; Number of entries. + DEFB #0B ; Key code: Cursor up. + DEFW L272E ; MENU-UP handler routine. + DEFB #0A ; Key code: Cursor down. + DEFW L2731 ; MENU-DOWN handler routine. + DEFB #07 ; Key code: Edit. + DEFW L2717 ; MENU-SELECT handler routine. + DEFB #0D ; Key code: Enter. + DEFW L2717 ; MENU-SELECT handler routine. + + +; ====================== +; MENU ROUTINES - PART 3 +; ====================== + +; ------------------------ +; Initialise Mode Settings +; ------------------------ +; Called before Main menu displayed. + +L2584: CALL L28BE ; Reset Cursor Position. + + LD HL,#0000 ; No top line. + LD (#FC9A),HL ; Line number at top of screen. + + LD A,#82 ; Signal waiting for key press, and menu is displayed. + LD (#EC0D),A ; Store the Editor flags. + + LD HL,#0000 ; No current line number. + LD (#5C49),HL ; E_PPC. Current line number. + + CALL L35BC ; Reset indentation settings. + CALL L365E ; Reset to 'L' Mode + RET ; [Could have saved one byte by using JP #365E (ROM 0)] + +; -------------- +; Show Main Menu +; -------------- + +L259F: LD HL,TSTACK ; #5BFF. + LD (OLDSP),HL ; #5B81. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD A,#02 ; Select main screen. + RST #28 ; + DEFW CHAN_OPEN ; #1601. +;[v]..................................................................................................................... SP2000 [v] +;L25AD: LD HL,L2744 ; Jump table for Main Menu. +L25AD: LD HL,M_COM ; Jump table for Main Menu. +;[^]..................................................................................................................... SP2000 [^] + LD (#F6EA),HL ; Store current menu jump table address. + LD HL,L2754 ; The Main Menu text. + LD (#F6EC),HL ; Store current menu text table address. + + PUSH HL ; Store address of menu on stack. + + LD HL,#EC0D ; Editor flags. + SET 1,(HL) ; Indicate 'menu displayed'. + RES 4,(HL) ; Signal return to main menu. + DEC HL ; Current menu index. + LD (HL),#00 ; Select top entry. + + POP HL ; Retrieve address of menu. + + CALL L36A8 ; Display menu and highlight first item. + + JP L2653 ; Jump ahead to enter the main key waiting and processing loop. + + +; ======================== +; EDITOR ROUTINES - PART 2 +; ======================== + +; ----------------------------------------------- +; Return to Editor / Calculator / Menu from Error +; ----------------------------------------------- + +L25CB: LD IX,#FD6C ; Point IX at editing settings information. + + LD HL,TSTACK ; #5BFF. + LD (OLDSP),HL ; #5B81. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD A,#02 ; + RST #28 ; + DEFW CHAN_OPEN ; #1601. Select main screen. + + CALL L3668 ; Reset 'L' mode. + LD HL,#5C3B ; FLAGS. + +L25E3: BIT 5,(HL) ; Has a key been pressed? + JR Z,L25E3 ; Wait for a key press. + + LD HL,#EC0D ; Editor flags. + RES 3,(HL) ; Signal line has not been altered. + + BIT 6,(HL) ; Is editing area the lower screen? + JR NZ,L2604 ; If so then skip printing a banner and jump ahead to return to the Editor. + + LD A,(#EC0E) ; Fetch mode. + CP #04 ; Calculator mode? + JR Z,L2601 ; Jump ahead if so. + + CP #00 ; Edit Menu mode? + JP NZ,L28C7 ; Jump if not to re-display Main menu. + +;Edit menu Print mode + + CALL L3848 ; Clear screen and print "128 BASIC" in the banner line. + JR L2604 ; Jump ahead to return to the Editor. + +;Calculator mode + +L2601: CALL L384D ; Clear screen and print "Calculator" in the banner line. + +; -------------------- +; Return to the Editor +; -------------------- +; Either as the result of a re-listing, an error or from completing the Edit Menu Print option. + +; [*BUG* - Occurs only with ZX Interface 1 attached and a BASIC line such as 1000 OPEN #4, "X" (the line number must be greater than 999). +; This produces the error message "Invalid device expression, 1000:1" but the message is too long to fit on a single line. When using the lower screen +; for editing, spurious effects happen to the bottom lines. When using the full screen editor, a crash occurs. Credit: Toni Baker, ZX Computing Monthly] + +; [The bug is caused by system variable DF_SZ being increased to 3 as a result of the error message spilling onto an extra line. The error can be resolved +; by inserting a LD (IY+#31),#02 instruction at #2604 (ROM 0). Credit: Paul Farrow] + +L2604: CALL L30D6 ; Reset Below-Screen Line Edit Buffer settings to their default values. + CALL L3222 ; Reset Above-Screen Line Edit Buffer settings to their default values. + + LD A,(#EC0E) ; Fetch the mode. + CP #04 ; Calculator mode? + JR Z,L2653 ; Jump ahead if not to wait for a key press. + +;Calculator mode + + LD HL,(#5C49) ; E_PPC. Fetch current line number. + LD A,H ; + OR L ; Is there a current line number? + JR NZ,L262D ; Jump ahead if so. + + LD HL,(#5C53) ; PROG. Address of start of BASIC program. + LD BC,(#5C4B) ; VARS. Address of start of variables area. + AND A ; + SBC HL,BC ; HL=Length of program. + JR NZ,L262A ; Jump if a program exists. + +;No program exists + + LD HL,#0000 ; + LD (#EC08),HL ; Set no line number last edited. + +L262A: LD HL,(#EC08) ; Fetch line number of last edited line. + +L262D: CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + RST #28 ; Find address of line number held in HL, or the next line if it does not exist. + DEFW LINE_ADDR ; #196E. Return address in HL. + RST #28 ; Find line number for specified address, and return in DE. + DEFW LINE_NO ; #1695. Fetch the line number for the line found. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD (#5C49),DE ; E_PPC. Save the current line number. + + LD HL,#EC0D ; Editor flags. + BIT 5,(HL) ; Process the BASIC line? + JR NZ,L2653 ; Jump ahead if calculator mode. + + LD HL,#0000 ; + LD (#EC06),HL ; Signal no editable characters in the line prior to the cursor. + + CALL L152F ; Relist the BASIC program. + + CALL L29F2 ; Set attribute at editing position so as to show the cursor. + CALL L2944 ; Call the ENTER handler routine. + +; ----------------- +; Main Waiting Loop +; ----------------- +; Enter a loop to wait for a key press. Handles key presses for menus, the Calculator and the Editor. + +L2653: LD SP,TSTACK ; #5BFF. Use temporary stack. + + CALL L3668 ; Reset 'L' mode. + + CALL L367F ; Wait for a key. [Note that it is possible to change CAPS LOCK mode whilst on a menu] + PUSH AF ; Save key code. + + LD A,(#5C39) ; PIP. Tone of keyboard click. + CALL L26EC ; Produce a key click noise. + + POP AF ; Retrieve key code. + CALL L2669 ; Process the key press. + + JR L2653 ; Wait for another key. + +; ----------------- +; Process Key Press +; ----------------- +; Handle key presses for the menus and the Editor. +; Entry: A=Key code. +; Zero flag set if a menu is being displayed. + +L2669: LD HL,#EC0D ; Editor flags. + BIT 1,(HL) ; Is a menu is displayed? + PUSH AF ; Save key code and flags. +;[v]..................................................................................................................... SP2000 [v] +; LD HL,L2577 ; Use menu keys lookup table. + LD HL,ED_COM ; +;[^]..................................................................................................................... SP2000 [^] + JR NZ,L2677 ; Jump if menu is being displayed. + + LD HL,L2537 ; Use editing keys lookup table. + +L2677: CALL L3FCE ; Find and call the action handler for this key press. + JR NZ,L2681 ; Jump ahead if no match found. + + CALL NC,L26E7 ; If required then produce error beep. + + POP AF ; Restore key code. + RET ; + +;No action defined for key code + +L2681: POP AF ; Restore key code and flags. + JR Z,L2689 ; Jump if menu is not being displayed. + +;A menu is being displayed, so just ignore key press + + XOR A ; Select 'L' mode. + LD (#5C41),A ; MODE. + RET ; + +;A menu is not being displayed + +L2689: LD HL,#EC0D ; Editor flags. + BIT 0,(HL) ; Is the Screen Line Edit Buffer is full? + JR Z,L2694 ; Jump if not to process the key code. + +;The buffer is full so ignore the key press + + CALL L26E7 ; Produce error beep. + RET ; [Could have save a byte by using JP #26E7 (ROM 0)] + +L2694: CP #A3 ; Was it a supported function key code? + JR NC,L2653 ; Ignore by jumping back to wait for another key. + ; [*BUG* - This should be RET NC since it was called from the loop at #2653 (ROM 0). Repeatedly pressing an unsupported + ; key will result in a stack memory leak and eventual overflow. Credit: John Steven (+3), Paul Farrow (128)] + JP L28F1 ; Jump forward to handle the character key press. + +; -------------------------- +; TOGGLE Key Handler Routine +; -------------------------- +; Toggle between editing in the lower and upper screen areas. +; Also used by the editing menu SCREEN option. + +L269B: LD A,(#EC0E) ; Fetch mode. + CP #04 ; Calculator mode? + RET Z ; Return if so (TOGGLE has no effect in Calculator mode). + + CALL L1630 ; Clear Editing Display. + + LD HL,#EC0D ; Editor flags. + RES 3,(HL) ; Reset 'line altered' flag. + + LD A,(HL) ; + XOR #40 ; Toggle screen editing area flag. + LD (HL),A ; + AND #40 ; + JR Z,L26B6 ; Jump forward if the editing area is now the upper area. + + CALL L26BB ; Set the lower area as the current editing area. + JR L26B9 ; Jump forward. + +L26B6: CALL L26CE ; Set the upper area as the current editing area. + +L26B9: SCF ; Signal do not produce an error beep. + RET ; + +; ------------------- +; Select Lower Screen +; ------------------- +; Set the lower screen as the editing area. + +L26BB: CALL L3881 ; Clear lower editing area display. + LD HL,#EC0D ; Editor flags. + SET 6,(HL) ; Signal using lower screen. + + CALL L2E2D ; Reset to lower screen. + CALL L3A88 ; Set default lower screen editing cursor settings. + CALL L28DF ; Set default lower screen editing settings. + + JR L26D9 ; Jump ahead to continue. + +; ------------------- +; Select Upper Screen +; ------------------- +; Set the upper screen as the editing area. + +L26CE: LD HL,#EC0D ; Editor flags. + RES 6,(HL) ; Signal using main screen. + + CALL L28BE ; Reset Cursor Position. + CALL L3848 ; Clear screen and print the "128 BASIC" banner line. + +L26D9: LD HL,(#FC9A) ; Line number at top of screen. + LD A,H ; + OR L ; Is there a line? + CALL NZ,L334A ; If there is then get the address of BASIC line for this line number. + CALL L152F ; Relist the BASIC program. + JP L29F2 ; Set attribute at editing position so as to show the cursor, and return. + +; ------------------ +; Produce Error Beep +; ------------------ +; This is the entry point to produce the error beep, e.g. when trying to cursor up or down past the BASIC program. +; It produces a different tone and duration from the error beep of 48K mode. The change is pitch is due to the SRL A +; instruction at #26EA (ROM 0), and the change in duration is due to the instruction at #26F1 (ROM 0) which loads HL with #0C80 as opposed +; to #1A90 which is used when in 48K mode. The key click and key repeat sounds are produced by entering at #26EC (ROM 0) but with A +; holding the value of system variable PIP. This produces the same tone as 48K mode but is of a much longer duration due to +; HL being loaded with #0C80 as opposed to the value of #00C8 used in 48K mode. The Spanish 128 uses the same key click tone +; and duration in 128K mode as it does in 48K mode, leading to speculation that the Spectrum 128 (and subsequent models) should +; have done the same and hence suffer from a bug. However, there is no reason why this should be the case, and it can easily be +; imagined that the error beep note duration of 48K mode would quickly become very irritating when in 128K mode where it is likely +; to occur far more often. Hence the reason for its shorter duration. The reason for the longer key click is less clear, unless it +; was to save memory by using a single routine. However, it would only have required an additional 3 bytes to set HL independently +; for key clicks, which is not a great deal considering there is 1/2K of unused routines at #2336 (ROM 0). Since the +; INPUT command is handled by ROM 1, it produces key clicks at the 48K mode duration even when executed from 128 BASIC mode. + +L26E7: LD A,(#5C38) ; RASP. + SRL A ; Divide by 2. + +;This entry point is called to produce the key click tone. In 48K mode, the key click sound uses an HL value of #00C8 +;and so is 16 times shorter than in 128K mode. + +L26EC: PUSH IX ; + + LD D,#00 ; Pitch. + LD E,A ; + LD HL,#0C80 ; Duration. + +L26F4: RST #28 ; + DEFW BEEPER ; #03B5. Produce a tone. + + POP IX ; + RET ; + +; -------------------- +; Produce Success Beep +; -------------------- + +L26FA: PUSH IX ; + + LD DE,#0030 ; Frequency*Time. + LD HL,#0300 ; Duration. + JR L26F4 ; Jump to produce the tone. + + +; ====================== +; MENU ROUTINES - PART 4 +; ====================== + +; =============================== +; Menu Key Press Handler Routines +; =============================== + +; ----------------------------- +; Menu Key Press Handler - MENU +; ----------------------------- +; This is executed when the EDIT key is pressed, either from within a menu or from the BASIC editor. + +L2704: CALL L29EC ; Remove cursor, restoring old attribute. + + LD HL,#EC0D ; HL points to Editor flags. + SET 1,(HL) ; Signal 'menu is being displayed'. + DEC HL ; HL=#EC0C. + LD (HL),#00 ; Set 'current menu item' as the top item. + +L270F: LD HL,(#F6EC) ; Address of text for current menu. + CALL L36A8 ; Display menu and highlight first item. + + SCF ; Signal do not produce an error beep. + RET ; + +; ------------------------------- +; Menu Key Press Handler - SELECT +; ------------------------------- + +L2717: LD HL,#EC0D ; HL points to Editor flags. + RES 1,(HL) ; Clear 'displaying menu' flag. + + DEC HL ; HL=#EC0C. + LD A,(HL) ; A=Current menu option index. + + LD HL,(#F6EA) ; HL points to jump table for current menu. + PUSH HL ; + PUSH AF ; + CALL L373E ; Restore menu screen area. + POP AF ; + POP HL ; + + CALL L3FCE ; Call the item in the jump table corresponding to the + ; currently selected menu item. + JP L29F2 ; Set attribute at editing position so as to show the cursor, and return. + +; ---------------------------------- +; Menu Key Press Handler - CURSOR UP +; ---------------------------------- + +L272E: SCF ; Signal move up. + JR L2732 ; Jump ahead to continue. + +; ------------------------------------ +; Menu Key Press Handler - CURSOR DOWN +; ------------------------------------ +L2731: AND A ; Signal moving down. + +L2732: LD HL,#EC0C ; + LD A,(HL) ; Fetch current menu index. + PUSH HL ; Save it. + + LD HL,(#F6EC) ; Address of text for current menu. + CALL C,L37A7 ; Call if moving up. + CALL NC,L37B6 ; Call if moving down. + + POP HL ; HL=Address of current menu index store. + LD (HL),A ; Store the new menu index. + +; Comes here to complete handling of Menu cursor up and down. Also as the handler routines +; for Edit Menu return to 128 BASIC option and Calculator menu return to Calculator option, +; which simply make a return. + +L2742: SCF ; + RET ; + + +; =========== +; Menu Tables +; =========== + +; --------- +; Main Menu +; --------- +; Jump table for the main 128K menu, referenced at #25AD (ROM 0). +;!FIXIT возможно, что таблица не используется уже +L2744: DEFB #05 ; Number of entries. + DEFB #00 + ;DEFW L2831 ; Tape Loader option handler. + DEFW #2831 ; Tape Loader option handler. + DEFB #01 + DEFW L286C ; 128 BASIC option handler. + DEFB #02 + DEFW L2885 ; Calculator option handler. + DEFB #03 + DEFW L1B47 ; 48 BASIC option handler. + DEFB #04 + DEFW L2816 ; Tape Tester option handler. + +;[v]..................................................................................................................... SP2000 [v] +; Text for the main 128K menu +; L2754: DEFB #06 ; Number of entries. +; DEFM "128 " ; Menu title. +; DEFB #FF +; L275E: DEFM "Tape Loade" +; DEFB 'r'+#80 +; L2769: DEFM "128 BASI" +; DEFB 'C'+#80 +; L2772: DEFM "Calculato" +; DEFB 'r'+#80 +; DEFM "48 BASI" +; DEFB 'C'+#80 +; L2784: DEFM "Tape Teste" +; DEFB 'r'+#80 + +; DEFB ' '+#80 ; #A0. End marker. + +; DC - every last character of a string will have bit 7 set +; Text for the main 128K menu +L2754: +M_TST: DB 7 ; Number of entries. + DB 'Sprinter',#FF ; Menu title. + DC 'TR-DOS' +BISD: DC 'Hardware' +B128: DC '128 BASIC' +BCALC: DC 'Calculator' + DC '48 BASIC' +BSERV: DC 'Options' + DC ' ' ; #20A0. End marker. +;[^]..................................................................................................................... SP2000 [^] +; --------- +; Edit Menu +; --------- + +; Jump table for the Edit menu + +L2790: DEFB #05 ; Number of entries. + DEFB #00 + DEFW L2742 ; (Return to) 128 BASIC option handler. + DEFB #01 + DEFW L2851 ; Renumber option handler. + DEFB #02 + DEFW L2811 ; Screen option handler. + DEFB #03 + DEFW L2862 ; Print option handler. + DEFB #04 + DEFW L281C ; Exit option handler. + +; Text for the Edit menu + +L27A0: DEFB #06 ; Number of entries. + DEFM "Options " + DEFB #FF + DEFM "128 BASI" + DEFB 'C'+#80 + DEFM "Renumbe" + DEFB 'r'+#80 + DEFM "Scree" + DEFB 'n'+#80 + DEFM "Prin" + DEFB 't'+#80 + DEFM "Exi" + DEFB 't'+#80 + + DEFB ' '+#80 ; #A0. End marker. + +; --------------- +; Calculator Menu +; --------------- + +; Jump table for the Calculator menu + +L27CB: DEFB #02 ; Number of entries. + DEFB #00 + DEFW L2742 ; (Return to) Calculator option handler. + DEFB #01 + DEFW L281C ; Exit option handler. + +; Text for the Calculator menu + +L27D2: DEFB 03 ; Number of entries. + DEFM "Options " + DEFB #FF + DEFM "Calculato" + DEFB 'r'+#80 + DEFM "Exi" + DEFB 't'+#80 + + DEFB ' '+#80 ; #A0. End marker. + +; ---------------- +; Tape Loader Text +; ---------------- +;[v]..................................................................................................................... SP2000 [v] +;!TODO разобраться +; L27EB: DEFB #16, #01, #00 ; AT 1,0; +; DEFB #10, #00 ; INK 0; +; DEFB #11, #07 ; PAPER 7; +; DEFB #13, #00 ; BRIGHT 1; +; DEFM "To cancel " +L27EB: +M_COM: BYTE 6,0 + WORD TRDOS + BYTE 1 + WORD HARDWARE ;ISDOS + BYTE 2 + WORD L286C + BYTE 3 + WORD L2885 + BYTE 4 + WORD L1B47 + BYTE 5 + WORD SERVICE + +;!FIXIT ненужный хвост + DEFM "- press BREAK twic" + DEFB 'e'+#80 +;[^]..................................................................................................................... SP2000 [^] +; + + BLOCK #2811-$,0 +; ===================== +; Menu Handler Routines +; ===================== + +; ------------------------- +; Edit Menu - Screen Option +; ------------------------- + +L2811: CALL L269B ; Toggle between editing in the lower and upper screen areas. + JR L2874 ; Jump ahead. + +;[v]..................................................................................................................... SP2000 [v] +; ------------------------------ +; Main Menu - Tape Tester Option +; ------------------------------ +; L2816: CALL L3857 ; Clear screen and print the "Tape Tester" in the banner. +; CALL L3BE9 ; Run the tape tester, exiting via the 'Exit' option menu handler. +L2816: CALL CLS_X + JP L3BEC +;[^]..................................................................................................................... SP2000 [^] + +; ----------------------------------------- +; Edit Menu / Calculator Menu - Exit Option +; ----------------------------------------- + +L281C: LD HL,#EC0D ; Editor flags. + RES 6,(HL) ; Indicate main screen editing. + CALL L28BE ; Reset Cursor Position. + + LD B,#00 ; Top row to clear. + LD D,#17 ; Bottom row to clear. + CALL L3B5E ; Clear specified display rows. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + JP L259F ; Jump back to show the menu. + +;[v]..................................................................................................................... SP2000 [v] +; ------------------------------ +; Main Menu - Tape Loader Option +; ------------------------------ +; L2831: CALL L3852 ; Clear screen and print "Tape Loader" in the banner line. + +; LD HL,#5C3C ; TVFLAG. +; SET 0,(HL) ; Signal using lower screen area. + +; LD DE,L27EB ; Point to message "To cancel - press BREAK twice". +; CALL L057D ; Print the text. + +; RES 0,(HL) ; Signal using main screen area. +; SET 6,(HL) ; [This bit is unused in the 48K Spectrum and only ever set in 128K mode via the Tape Loader option. +; ; It is never subsequently tested or reset. It may have been the intention to use this to indicate that +; ; the screen requires clearing after loading to remove the "Tape Loader" banner and the lower screen +; ; message "To cancel - press BREAK twice"] + +; LD A,#07 ; Tape Loader mode. +; LD (#EC0E),A ; [Redundant since call to #1AF1 (ROM 0) will set it to #FF] + +; LD BC,#0000 ; +; CALL L372B ; Perform 'Print AT 0,0;'. + +; JP L1AF1 ; Run the tape loader. +L2831: +ED_COM: BYTE 10 ; Number of entries. + BYTE #0B ; Key code: Cursor up. + DEFW L272E ; MENU-UP handler routine. + BYTE #0A ; Key code: Cursor dow + DEFW L2731 ; MENU-DOWN handler routine. + BYTE #0D ; Key code: Enter. + DEFW L2717 ; MENU-SELECT handler routine. + BYTE '8' ; на синклер-джойстике вниз + DEFW L2731 ; MENU-DOWN handler routine. + BYTE '9' ; на синклер-джойстике вверх + DEFW L272E ; MENU-UP handler routine. + BYTE '0' ; на синклер-джойстике enter + DEFW L2717 ; MENU-SELECT handler routine. + BYTE '6' ; на синклер-джойстике влево Page Up + DEFW LEFT_E ; MENU-PageUp + BYTE #08 ; Key code: Cursor left + DEFW LEFT ; MENU-PageUp + BYTE '7' ; [x] было 7 + DEFW RIGHT_E ; MENU-PageDown + BYTE #09 ; Key code: Cursor right + DEFW RIGHT ; MENU-PageDown + + ; DB #1A ; просто стереть эту строчку, не нужна +;[^]..................................................................................................................... SP2000 [^] + + BLOCK #2851-$,0 +; --------------------------- +; Edit Menu - Renumber Option +; --------------------------- + +L2851: CALL L3888 ; Run the renumber routine. + CALL NC,L26E7 ; If not successful then produce error beep if required. + + LD HL,#0000 ; There is no current line number. + LD (#5C49),HL ; E_PPC. Current line number. + LD (#EC08),HL ; Temporary E_PPC used by BASIC Editor. + + JR L2865 ; Jump ahead to display the "128 BASIC" banner if required, set the menu mode and return. + +; ------------------------ +; Edit Menu - Print Option +; ------------------------ + +L2862: CALL L1B14 ; Perform an LLIST. + +;Edit Menu - Renumber option joins here + +L2865: LD HL,#EC0D ; Editor flags. + BIT 6,(HL) ; Using lower editing screen? + JR NZ,L2874 ; Jump ahead if so. + +L286C: LD HL,#5C3C ; TVFLAG. + RES 0,(HL) ; Allow leading space. + CALL L3848 ; Clear screen and print the "128 BASIC" banner line. + +;Edit Menu - Screen option joins here + +L2874: LD HL,#EC0D ; Editor flags. + RES 5,(HL) ; Signal not to process the BASIC line. + RES 4,(HL) ; Signal return to main menu. + + LD A,#00 ; Select Edit menu mode. [Could have saved 1 byte by using XOR A] + LD HL,L2790 ; Edit Menu jump table. + LD DE,L27A0 ; Edit Menu text table. + JR L28B1 ; Store the new mode and menu details. + +; ----------------------------- +; Main Menu - Calculator Option +; ----------------------------- + +L2885: LD HL,#EC0D ; Editor flags. + SET 5,(HL) ; Signal to process the BASIC line. + SET 4,(HL) ; Signal return to calculator. + RES 6,(HL) ; Signal editing are is the main screen. + + CALL L28BE ; Reset cursor position. + + CALL L384D ; Clear screen and print "Calculator" in the banner line. + + LD A,#04 ; Set calculator mode. + LD (#EC0E),A ; Store mode. + + LD HL,#0000 ; No current line number. + LD (#5C49),HL ; E_PPC. Store current line number. + + CALL L152F ; Relist the BASIC program. + + LD BC,#0000 ; B=Row. C=Column. Top left of screen. + LD A,B ; Preferred column. + CALL L29F8 ; Store editing position and print cursor. + + LD A,#04 ; Select calculator mode. + LD HL,L27CB ; Calculator Menu jump table + LD DE,L27D2 ; Calculator Menu text table + +;Edit Menu - Print option joins here + +L28B1: LD (#EC0E),A ; Store mode. + LD (#F6EA),HL ; Store address of current menu jump table. + LD (#F6EC),DE ; Store address of current menu text. + JP L2604 ; Return to the Editor. + + +; ======================== +; EDITOR ROUTINES - PART 3 +; ======================== + +; --------------------- +; Reset Cursor Position +; --------------------- + +L28BE: CALL L2E1F ; Reset to main screen. + CALL L3A7F ; Set default main screen editing cursor details. + JP L28E8 ; Set default main screen editing settings. + +; ------------------- +; Return to Main Menu +; ------------------- + +L28C7: LD B,#00 ; Top row of editing area. + LD D,#17 ; Bottom row of editing area. + CALL L3B5E ; Clear specified display rows. + + JP L25AD ; Jump to show Main menu. + +; --------------------------------- +; Main Screen Error Cursor Settings +; --------------------------------- +; Main screen editing cursor settings. +; Gets copied to #F6EE. + +L28D1: DEFB #06 ; Number of bytes in table. + DEFB #00 ; #F6EE = Cursor position - row 0. + DEFB #00 ; #F6EF = Cursor position - column 0. + DEFB #00 ; #F6F0 = Cursor position - column 0 preferred. + DEFB #04 ; #F6F1 = Top row before scrolling up. + DEFB #10 ; #F6F2 = Bottom row before scrolling down. + DEFB #14 ; #F6F3 = Number of rows in the editing area. + +; --------------------------------- +; Lower Screen Good Cursor Settings +; --------------------------------- +; Lower screen editing cursor settings. +; Gets copied to #F6EE. + +L28D8: DEFB #06 ; Number of bytes in table. + DEFB #00 ; #F6EE = Cursor position - row 0. + DEFB #00 ; #F6EF = Cursor position - column 0. + DEFB #00 ; #F6F0 = Cursor position - column 0 preferred. + DEFB #00 ; #F6F1 = Top row before scrolling up. + DEFB #01 ; #F6F2 = Bottom row before scrolling down. + DEFB #01 ; #F6F3 = Number of rows in the editing area. + +; ---------------------------------------- +; Initialise Lower Screen Editing Settings +; ---------------------------------------- +; Used when selecting lower screen. Copies 6 bytes from #28D9 (ROM 0) to #F6EE. + +L28DF: LD HL,L28D8 ; Default lower screen editing information. + LD DE,#F6EE ; Editing information stores. + JP L3FBA ; Copy bytes. + +; --------------------------------------- +; Initialise Main Screen Editing Settings +; --------------------------------------- +; Used when selecting main screen. Copies 6 bytes from #28D2 (ROM 0) to #F6EE. + +L28E8: LD HL,L28D1 ; Default main screen editing information. + LD DE,#F6EE ; Editing information stores. + JP L3FBA ; Copy bytes. + +; ------------------------------- +; Handle Key Press Character Code +; ------------------------------- +; This routine handles a character typed at the keyboard, inserting it into the Screen Line Edit Buffer +; as appropriate. +; Entry: A=Key press character code. + +L28F1: LD HL,#EC0D ; Editor flags. + OR A ; Clear carry flag. [Redundant instruction since carry flag return state never checked] + OR A ; [Redundant instruction] + BIT 0,(HL) ; Is the Screen Line Edit Buffer is full? + JP NZ,L29F2 ; Jump if it is to set attribute at editing position so as to show the cursor, and return. + + RES 7,(HL) ; Signal got a key press. + SET 3,(HL) ; Signal current line has been altered. + + PUSH HL ; Save address of the flags. + PUSH AF ; Save key code. + + CALL L29EC ; Remove cursor, restoring old attribute. + + POP AF ; + PUSH AF ; Get and save key code. + + CALL L2E81 ; Insert the character into the Screen Line Edit Buffer. + + POP AF ; Get key code. + + LD A,B ; B=Current cursor column position. + CALL L2B78 ; Find next Screen Line Edit Buffer editable position to right, moving to next row if necessary. + + POP HL ; Get address of the flags. + SET 7,(HL) ; Signal wait for a key. + JP NC,L29F2 ; Jump if new position not available to set cursor attribute at existing editing position, and return. + + LD A,B ; A=New cursor column position. + JP C,L29F8 ; Jump if new position is editable to store editing position and print cursor. + ; [This only needs to be JP #29F8 (ROM 0), thereby saving 3 bytes, since a branch to #29F2 (ROM 0) would have been taken above if the carry flag was reset] + JP L29F2 ; Set attribute at editing position so as to show the cursor, and return. + +; -------------------------------- +; DELETE-RIGHT Key Handler Routine +; -------------------------------- +; Delete a character to the right. An error beep is not produced if there is nothing to delete. +; +; Symbol: DEL +; --> +; +; Exit: Carry flag set to indicate not to produce an error beep. + +L291B: LD HL,#EC0D ; HL points to Editor flags. + SET 3,(HL) ; Indicate 'line altered'. + + CALL L29EC ; Remove cursor, restoring old attribute. Exit with C=row, B=column. + + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + + SCF ; Signal do not produce an error beep. + LD A,B ; A=The new cursor editing position. + JP L29F8 ; Store editing position and print cursor, and then return. + +; -------------------------- +; DELETE Key Handler Routine +; -------------------------- +; Delete a character to the left. An error beep is not produced if there is nothing to delete. +; +; Symbol: DEL +; <-- +; +; Exit: Carry flag set to indicate not to produce an error beep. + +L292B: LD HL,#EC0D ; HL points to Editor flags. + RES 0,(HL) ; Signal that the Screen Line Edit Buffer is not full. + SET 3,(HL) ; Indicate 'line altered'. + + CALL L29EC ; Remove cursor, restoring old attribute. Exit with C=row, B=column. + + CALL L2B5B ; Select previous column position (Returns carry flag set if editable). + CCF ; Signal do not produce an error beep if not editable. + JP C,L29F2 ; Jump if not editable to set attribute at editing position so as to show the cursor, and return. + + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + + SCF ; Signal do not produce an error beep. + LD A,B ; A=The new cursor editing position. + JP L29F8 ; Store editing position and print cursor, and then return. + +; ------------------------- +; ENTER Key Handler Routine +; ------------------------- +; This routine handles ENTER being pressed. If not on a BASIC line then it does nothing. If on an +; unaltered BASIC line then insert a blank row after it and move the cursor to it. If on an altered +; BASIC line then attempt to enter it into the BASIC program, otherwise return to produce an error beep. +; Exit: Carry flag reset to indicate to produce an error beep. + +L2944: CALL L29EC ; Remove cursor, restoring old attribute. + + PUSH AF ; Save preferred column number. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + PUSH BC ; Stack current editing position. + LD B,#00 ; Column 0. + CALL L2E41 ; Is this a blank row? i.e. Find editable position on this row to the right, returning column number in B. + POP BC ; Retrieve current editing position. + JR C,L295E ; Jump ahead if editable position found, i.e. not a blank row. + +;No editable characters on the row, i.e. a blank row + + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + LD A,(HL) ; Fetch the flag byte. + CPL ; Invert it. + AND #09 ; Keep the 'first row' and 'last row' flags. + JR Z,L297A ; Jump if both flags were set indicating not on a BASIC line. + +;On a BASIC line + +L295E: LD A,(#EC0D) ; Editor flags. + BIT 3,A ; Has the current line been altered? + JR Z,L296A ; Jump ahead if not. + +;The current BASIC line has been altered + + CALL L2C8E ; Enter line into program. + JR NC,L297F ; Jump if syntax error to produce an error beep. + +L296A: CALL L2C4C ; Find end of the current BASIC line in the Screen Line Edit Buffer, scrolling up rows as required. Returns column number into B. + CALL L2B78 ; Find address of end position in current BASIC line. Returns address into HL. + CALL L2ECE ; Insert a blank line in the Screen Line Edit Buffer, shifting subsequent rows down. + +;Display the cursor on the first column of the next row + + LD B,#00 ; First column. + POP AF ; A=Preferred column number. + SCF ; Signal do not produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + +;Cursor is on a blank row, which is not part of a BASIC line + +L297A: POP AF ; Discard stacked item. + SCF ; Signal do not produce an error beep. + JP L29F2 ; Set attribute at current editing position so as to show the cursor, and return. + +;A syntax error occurred so return signalling to produce an error beep + +L297F: POP AF ; Discard stacked item. + JP L29F2 ; Set attribute at current editing position so as to show the cursor, and return. + +; ---------------------------------- +; TOP-OF-PROGRAM Key Handler Routine +; ---------------------------------- +; Move to the first row of the first line of the BASIC program. An error beep is not produced if there is no program. +; +; Symbol: ------ +; / \/ \ +; | | +; +; Exit: Carry flag set to indicate not to produce an error beep. + +L2983: LD A,(#EC0E) ; Fetch mode. + CP #04 ; Calculator mode? + RET Z ; Exit if so. + +;Editor mode + + CALL L29EC ; Remove cursor, restoring old attribute. + + LD HL,#0000 ; The first possible line number. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + RST #28 ; Find address of line number 0, or the next line if it does not exist. + DEFW LINE_ADDR ; #196E. Return address in HL. + RST #28 ; Find line number for specified address, and return in DE. + DEFW LINE_NO ; #1695. DE=Address of first line in the BASIC program. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD (#5C49),DE ; E_PPC. Store the current line number. + + LD A,#0F ; Paper 1, Ink 7 - Blue. + CALL L3A96 ; Set the cursor colour. + + CALL L152F ; Relist the BASIC program. + SCF ; Signal do not produce an error beep. + JP L29F2 ; Set attribute at editing position so as to show the cursor, and return. + +; ---------------------------------- +; END-OF-PROGRAM Key Handler Routine +; ---------------------------------- +; Move to the last row of the bottom line of the BASIC program. An error beep is not produced if there is no program. +; +; Symbol: | | +; \ /\ / +; ------ +; +; Exit: Carry flag set to indicate not to produce an error beep. + +L29AB: LD A,(#EC0E) ; Fetch mode. + CP #04 ; Calculator mode? + RET Z ; Exit if so. + +;Editor mode + + CALL L29EC ; Remove cursor, restoring old attribute. + + LD HL,#270F ; The last possible line number, 9999. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + RST #28 ; Find address of line number 9999, or the previous line if it does not exist. + DEFW LINE_ADDR ; #196E. Return address in HL. + + EX DE,HL ; DE=Address of last line number. + + RST #28 ; Find line number for specified address, and return in DE. + DEFW LINE_NO ; #1695. DE=Address of last line in the BASIC program. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD (#5C49),DE ; E_PPC. Store the current line number. + + LD A,#0F ; Paper 1, Ink 7 - Blue. + CALL L3A96 ; Set the cursor colour. + + CALL L152F ; Relist the BASIC program. + SCF ; Signal do not produce an error beep. + JP L29F2 ; Set attribute at editing position so as to show the cursor, and return. + +; ----------------------------- +; WORD-LEFT Key Handler Routine +; ----------------------------- +; This routine moves to the start of the current word that the cursor is on, or if it is on the first +; character of a word then it moves to the start of the previous word. If there is no word to move to +; then signal to produce an error beep. +; +; Symbol: <-- +; <-- +; +; Exit: Carry flag reset to indicate to produce an error beep. + +L29D4: CALL L29EC ; Remove cursor, restoring old attribute. + + CALL L2BEA ; Find start of the current word to the left. + JP NC,L29F2 ; Jump if no word to the left to restore cursor attribute at current editing position, and return. + ; [Could have saved 4 bytes by joining the routine below, i.e. JR #29E7] + + LD A,B ; A=New cursor column number. Carry flag is set indicating not to produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + +; ------------------------------ +; WORD-RIGHT Key Handler Routine +; ------------------------------ +; This routine moves to the start of the next word. If there is no word to move to then signal to produce an error beep. +; +; Symbol: --> +; --> +; +; Exit: Carry flag reset to indicate to produce an error beep. + +L29E1: CALL L29EC ; Remove cursor, restoring old attribute. + + CALL L2C09 ; Find start of the current word to the right. + JR NC,L29F2 ; Jump if no word to the right to restore cursor attribute at current editing position, and return. + + LD A,B ; A=The new cursor editing column number. Carry is set indicating not to produce an error beep. + JR L29F8 ; Store editing position and print cursor, and then return. + +; ------------- +; Remove Cursor +; ------------- +; Remove editing cursor colour from current position. +; Exit: C=row number. +; B=Column number. + +L29EC: CALL L2A07 ; Get current cursor position (C=row, B=column, A=preferred column). + JP L364F ; Restore previous colour to character square + +; ----------- +; Show Cursor +; ----------- +; Set editing cursor colour at current position. +; Exit: C=row number. +; B=Column number. + +L29F2: CALL L2A07 ; Get current cursor position (C=row, B=column, A=preferred column). + JP L3640 ; Set editing position character square to cursor colour to show it. + ; [Could have saved 1 byte by using a JR instruction to join the end of the routine below] + +; -------------- +; Display Cursor +; -------------- +; Set editing cursor position and colour and then show it. +; Entry: C=Row number. +; B=Column number. +; A=Preferred column number. + +L29F8: CALL L2A11 ; Store new editing position. + + PUSH AF ; + PUSH BC ; + + LD A,#0F ; Paper 1, Ink 7 - Blue. + CALL L3A96 ; Store new cursor colour. + + POP BC ; + POP AF ; + + JP L3640 ; Set editing position character square to cursor colour to show it. + +; --------------------- +; Fetch Cursor Position +; --------------------- +; Returns the three bytes of the cursor position. +; Exit : C=Row number. +; B=Column number +; A=Preferred column number. + +L2A07: LD HL,#F6EE ; Editing info. + LD C,(HL) ; Row number. + INC HL ; + LD B,(HL) ; Column number. + INC HL ; + LD A,(HL) ; Preferred column number. + INC HL ; + RET ; + +; --------------------- +; Store Cursor Position +; --------------------- +; Store new editing cursor position. +; Entry: C=Row number. +; B=Column number. +; A=Preferred column number. + +L2A11: LD HL,#F6EE ; Editing information. + LD (HL),C ; Row number. + INC HL ; + LD (HL),B ; Column number. + INC HL ; + LD (HL),A ; Preferred column number. + RET ; + +; -------------------------------------------------- +; Get Current Character from Screen Line Edit Buffer +; -------------------------------------------------- +; Entry: C=Row number. +; B=Column number. +; Exit : A=Character. + +L2A1A: PUSH HL ; + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD H,#00 ; [Could have saved 2 bytes by calling the unused routine at #2E7B (ROM 0)] + LD L,B ; + ADD HL,DE ; Point to the column position within the row. + LD A,(HL) ; Get character at this position. + + POP HL ; + RET ; + +; --------------------------------- +; TEN-ROWS-DOWN Key Handler Routine +; --------------------------------- +; Move down 10 rows within the BASIC program, attempting to place the cursor as close to the preferred column number as possible. +; An error beep is produced if there is not 10 rows below. +; +; Symbol: | | +; \ /\ / +; +; Exit: Carry flag reset to indicate to produce an error beep. + +L2A25: CALL L29EC ; Remove cursor, restoring old attribute. + LD E,A ; E=Preferred column. + + LD D,#0A ; The ten lines to move down. + +L2A2B: PUSH DE ; + CALL L2B30 ; Move down to the next row, shifting rows up as appropriate. If moving onto a new BASIC line then + POP DE ; insert the previous BASIC line into the BASIC program if it has been altered. Returns new row number in C. + JR NC,L29F2 ; Jump if there was no row below to set attribute at editing position so as to show the cursor, and return. + + LD A,E ; A=Preferred column. + CALL L2A11 ; Store cursor editing position. + + LD B,E ; B=Preferred column. + CALL L2AF9 ; Find closest Screen Line Edit Buffer editable position to the right else to the left, returning column number in B. + JR NC,L2A42 ; Jump if no editable position found on the row, i.e. a blank row. + + DEC D ; Decrement row counter. + JR NZ,L2A2B ; Repeat to move down to the next row. + + LD A,E ; A=Preferred column. + JR C,L29F8 ; Jump if editable row exists to store editing position and print cursor, and then return. + ; [Redundant check of the carry flag, should just be JR #29F8 (ROM 0)] + +;A blank row was found below, must be at the end of the BASIC program + +L2A42: PUSH DE ; + CALL L2B0B ; Move back up to the previous row. + POP DE ; + + LD B,E ; B=Preferred column. + CALL L2AF9 ; Find closest Screen Line Edit Buffer editable position to the right else to the left, returning column number in B. + + LD A,E ; A=Preferred column. + OR A ; Carry will be reset indicating to produce an error beep. + JR L29F8 ; Store editing position and print cursor, and then return. + +; ------------------------------- +; TEN-ROWS-UP Key Handler Routine +; ------------------------------- +; Move up 10 rows within the BASIC program, attempting to place the cursor as close to the preferred column number as possible. +; An error beep is produced if there is not 10 rows above. +; +; Symbol: / \/ \ +; | | +; +; Exit: Carry flag reset to indicate to produce an error beep. + +L2A4F: CALL L29EC ; Remove cursor, restoring old attribute. + LD E,A ; E=Preferred column. + + LD D,#0A ; The ten lines to move up. + +L2A55: PUSH DE ; + CALL L2B0B ; Move up to the previous row, shifting rows down as appropriate. If moving onto a new BASIC line then + POP DE ; insert the previous BASIC line into the BASIC program if it has been altered. + JR NC,L29F2 ; Jump if there was no row above to set cursor attribute colour at existing editing position, and return. + + LD A,E ; A=Preferred column. + CALL L2A11 ; Store cursor editing position. + + LD B,E ; B=Preferred column. + CALL L2B02 ; Find closest Screen Line Edit Buffer editable position to the left else right, return column number in B. + JR NC,L2A6D ; Jump if no editable positions were found in the row, i.e. it is a blank row. + + DEC D ; Decrement row counter. + JR NZ,L2A55 ; Repeat to move up to the previous row. + + LD A,E ; A=Preferred column. + JP C,L29F8 ; Jump if editable row exists to store editing position and print cursor, and then return. + ; [Redundant check of the carry flag, should just be JP #29F8 (ROM 0)] + +;A blank row was found above, must be at the start of the BASIC program [???? Can this ever be the case?] + +L2A6D: PUSH AF ; Save the preferred column number and the flags. + + CALL L2B30 ; Move back down to the next row. Returns new row number in C. + + LD B,#00 ; Column 0. + CALL L2BD4 ; Find editable position in the Screen Line Edit Buffer row to the right, return column position in B. + + POP AF ; A=Preferred column. Carry will be reset indicating to produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + +; ------------------------------- +; END-OF-LINE Key Handler Routine +; ------------------------------- +; Move to the end of the current BASIC line. An error beep is produced if there is no characters in the current BASIC line. +; +; Symbol: -->| +; -->| +; +; Exit: Carry flag reset to indicate to produce an error beep and set not to produce an error beep. + +L2A7A: CALL L29EC ; Remove cursor, restoring old attribute. + + CALL L2C4C ; Find the end of the current BASIC line in the Screen Line Edit Buffer. + JP NC,L29F2 ; Jump if a blank row to set attribute at existing editing position so as to show the cursor, and return. + + LD A,B ; A=The new cursor editing column number. Carry is set indicating not to produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + +; --------------------------------- +; START-OF-LINE Key Handler Routine +; --------------------------------- +; Move to the start of the current BASIC line. An error beep is produced if there is no characters in the current BASIC line. +; +; Symbol: |<-- +; |<-- +; +; Exit: Carry flag reset to indicate to produce an error beep. + +L2A87: CALL L29EC ; Remove cursor, restoring old attribute. + + CALL L2C31 ; Find the start of the current BASIC line in the Screen Line Edit Buffer. + JP NC,L29F2 ; Jump if a blank row to set attribute at existing editing position so as to show the cursor, and return. + + LD A,B ; A=The new cursor editing position. Carry is set indicating not to produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + +; ----------------------------- +; CURSOR-UP Key Handler Routine +; ----------------------------- +; Move up 1 row, attempting to place the cursor as close to the preferred column number as possible. +; An error beep is produced if there is no row above. +; Exit: Carry flag reset to indicate to produce an error beep. + +L2A94: CALL L29EC ; Remove cursor, restoring old attribute. + + LD E,A ; E=Preferred column. + PUSH DE ; + CALL L2B0B ; Move up to the previous row, shifting rows down as appropriate. If moving onto a new BASIC line then + POP DE ; insert the previous BASIC line into the BASIC program if it has been altered. + JP NC,L29F2 ; Jump if there was no row above to set cursor attribute colour at existing editing position, and return. + + LD B,E ; B=Preferred column. + CALL L2B02 ; Find closest Screen Line Edit Buffer editable position to the left else right, return column number in B. + LD A,E ; A=Preferred column. + JP C,L29F8 ; Jump if an editable position was found to store editing position and print cursor, and then return. + +;A blank row was found above, must be at the start of the BASIC program [???? Can this ever be the case?] + + PUSH AF ; Save the preferred column number and the flags. + + CALL L2B30 ; Move down to the next row, shifting rows up as appropriate. Returns new row number in C. + + LD B,#00 ; Column 0. + CALL L2AF9 ; Find closest Screen Line Edit Buffer editable position to the right. + + POP AF ; A=Preferred column. Carry flag is reset indicating to produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + +; ------------------------------- +; CURSOR-DOWN Key Handler Routine +; ------------------------------- +; Move down 1 row, attempting to place the cursor as close to the preferred column number as possible. +; An error beep is produced if there is no row below. +; Exit: Carry flag reset to indicate to produce an error beep. + +L2AB5: CALL L29EC ; Remove cursor, restoring old attribute. + + LD E,A ; E=Preferred column. + PUSH DE ; + CALL L2B30 ; Move down to the next row, shifting rows up as appropriate. If moving onto a new BASIC line then + POP DE ; insert the previous BASIC line into the BASIC program if it has been altered. Returns new row number in C. + JP NC,L29F2 ; Jump if there was no row below to set attribute at editing position so as to show the cursor, and return. + + LD B,E ; B=Preferred column. + CALL L2B02 ; Find closest Screen Line Edit Buffer editable position to the left else right, return column number in B. + LD A,E ; A=Preferred column. + JP C,L29F8 ; Jump if an editable position was found to store editing position and print cursor, and then return. + +;A blank row was found above, must be at the start of the BASIC program [???? Can this ever be the case?] + + PUSH DE ; Save the preferred column. + CALL L2B0B ; Move up to the previous row, shifting rows down as appropriate. + POP DE ; + + LD B,E ; B=Preferred column. + CALL L2AF9 ; Find closest Screen Line Edit Buffer editable position to the right else to the left, returning column number in B. + + LD A,E ; A=Preferred column. + OR A ; Reset carry flag to indicate to produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + +; ------------------------------- +; CURSOR-LEFT Key Handler Routine +; ------------------------------- +; Move left 1 character, stopping if the start of the first row of the first BASIC line is reached. +; An error beep is produced if there is no character to the left or no previous BASIC line to move to. +; Exit: Carry flag reset to indicate to produce an error beep. + +L2AD7: CALL L29EC ; Remove cursor, restoring old attribute. Returns with C=row, B=column. + + CALL L2B5B ; Find next Screen Line Edit Buffer editable position to left, wrapping to previous row as necessary. + JP C,L29F8 ; Jump if editable position found to store editing position and print cursor, and then return. + +;A blank row was found above, must be at the start of the BASIC program + + JP L29F2 ; Set cursor attribute at existing editing position, and return. Carry flag is reset indicating to produce an error beep. + +; -------------------------------- +; CURSOR-RIGHT Key Handler Routine +; -------------------------------- +; Move right 1 character, stopping if the end of the last row of the last BASIC line is reached. +; An error beep is produced if there is no character to the right or no next BASIC line to move to. +; Exit: Carry flag reset to indicate to produce an error beep. + +L2AE3: CALL L29EC ; Remove cursor, restoring old attribute. + + CALL L2B78 ; Find next Screen Line Edit Buffer editable position to right, wrapping to next row if necessary. + JP C,L29F8 ; Jump if editable position found to store editing position and print cursor, and then return. + +;A blank row was found below, must be at the end of the BASIC program + + PUSH AF ; Save the carry flag and preferred column number. + + CALL L2B0B ; Move up to the previous row, shifting rows down as appropriate. + + LD B,#1F ; Column 31. + CALL L2BDF ; Find the last editable column position searching to the left, returning the column number in B. (Returns carry flag set if there is one) + ; + POP AF ; Carry flag is reset indicating to produce an error beep. + JP L29F8 ; Store editing position and print cursor, and then return. + + +; ============================= +; Edit Buffer Routines - Part 1 +; ============================= + +; ----------------------------------------------------------------------------- +; Find Closest Screen Line Edit Buffer Editable Position to the Right else Left +; ----------------------------------------------------------------------------- +; This routine searches the specified Screen Line Edit Buffer row from the specified column to the right +; looking for the first editable position. If one cannot be found then a search is made to the left. +; Entry: B=Column number. +; Exit : Carry flag set if character at specified column is editable. +; B=Number of closest editable column. +; HL=Address of closest editable position. + +L2AF9: PUSH DE ; + + CALL L2BD4 ; Find Screen Line Edit Buffer editable position from previous column (or current column if the previous column does not exist) to the right, return column position in B. + CALL NC,L2BDF ; If no editable character found then search to the left for an editable character, return column position in B. + + POP DE ; + RET ; + +; ----------------------------------------------------------------------------- +; Find Closest Screen Line Edit Buffer Editable Position to the Left else Right +; ----------------------------------------------------------------------------- +; This routine searches the specified Screen Line Edit Buffer row from the specified column to the left +; looking for the first editable position. If one cannot be found then a search is made to the right. +; Entry: B=Column number. +; Exit : Carry flag set if character at specified column is editable. +; B=Number of closest editable column. +; HL=Address of closest editable position. + +L2B02: PUSH DE ; + + CALL L2BDF ; Find Screen Line Edit Buffer editable position to the left, returning column position in B. + CALL NC,L2BD4 ; If no editable character found then search from previous column (or current column if the previous column does not exist) to the right, return column position in B. + + POP DE ; + RET ; + +; ---------------------------------------------------------------------------------------------- +; Insert BASIC Line, Shift Edit Buffer Rows Down If Required and Update Display File If Required +; ---------------------------------------------------------------------------------------------- +; Called from the cursor up and down related key handlers. For example, when cursor up key is pressed the current +; BASIC line may need to be inserted into the BASIC program if it has been altered. It may also be necessary +; to shift all rows down should the upper scroll threshold be reached. If the cursor was on a blank row between BASIC +; lines then it is necessary to shift all BASIC lines below it up, i.e. remove the blank row. +; Entry: C=Current cursor row number in the Screen Line Edit Buffer. +; Exit : C=New cursor row number in the Screen Line Edit Buffer. +; Carry flag set if a new row was moved to. + +L2B0B: CALL L2C7C ; If current BASIC line has been altered and moved off of then insert it into the program. + JR NC,L2B2F ; Jump if BASIC line was not inserted. [Could have saved 1 byte by using RET NC] + + PUSH BC ; Save the new cursor row and column numbers. + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD B,#00 ; Column 0. + CALL L2E41 ; Is this a blank row? i.e. Find editable position on this row to the right, returning column number in B. + CALL NC,L2F80 ; If no editable position found then the cursor is on a blank row so shift all BASIC lines below it up to close the gap. + + POP BC ; Retrieve the new cursor row and column numbers. + + LD HL,#F6F1 ; Point to the editing area information. + LD A,(HL) ; Fetch the upper scroll threshold. + CP C ; Is it on the threshold? + JR C,L2B2D ; Jump if on a row below the threshold. + +;The upper row threshold for triggering scrolling the screen has been reached so proceed to scroll down one row + + PUSH BC ; Save the new cursor row and column numbers. + CALL L166F ; Shift all edit buffer rows down, and update display file if required. + POP BC ; + RET C ; Return if edit buffer rows were shifted. + +;The edit buffer rows were not shifted down + + LD A,C ; On the top row of the editing area? + OR A ; + RET Z ; Return with carry flag reset if on the top row. + +L2B2D: DEC C ; Move onto the previous row. + SCF ; Signal a new row was moved to. + +L2B2F: RET ; + +; -------------------------------------------------------------------------------------------- +; Insert BASIC Line, Shift Edit Buffer Rows Up If Required and Update Display File If Required +; -------------------------------------------------------------------------------------------- +; Called from the cursor up and down related key handlers. For example, when cursor down key is pressed the current +; BASIC line may need to be inserted into the BASIC program if it has been altered. It may also be necessary +; to shift all rows up should the lower scroll threshold be reached. If the cursor was on a blank row between BASIC +; lines then it is necessary to shift all BASIC lines below it up, i.e. remove the blank row. +; Entry: C=Current cursor row number in the Screen Line Edit Buffer. +; Exit : C=New cursor row number in the Screen Line Edit Buffer. +; Carry flag set if a new row was moved to. + +L2B30: PUSH BC ; Save row number. + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of row held in C, i.e. the new cursor row. + LD B,#00 ; Column 0. + CALL L2E41 ; Is this a blank row? i.e. Find editable position on this row to the right, returning column number in B. + + POP BC ; Get row number. + JR C,L2B3F ; Jump if editable position found, i.e. the row exists. [Could have saved 2 bytes by using JP NC,#2F80 (ROM 0)] + + JP L2F80 ; Cursor is on a blank row so shift all BASIC lines below it up to close the gap. + +L2B3F: CALL L2C68 ; Insert the BASIC Line into the BASIC program if the line has been altered. + JR NC,L2B5A ; Jump if the line was inserted into the program. [Could have saved 1 byte by using RET NC] + +;The BASIC line was not inserted into the program. C=New cursor row number, B=New cursor column number, A=New cursor preferred column number + + LD HL,#F6F1 ; Point to the editing area information. + INC HL ; Point to the 'Bottom Row Scroll Threshold' value. [Could have saved 1 byte by using LD HL,#F6F2] + LD A,C ; Fetch the new cursor row number. + CP (HL) ; Is it on the lower scroll threshold? + JR C,L2B58 ; Jump if on a row above the threshold. + +;The lower row threshold for triggering scrolling the screen has been reached so proceed to scroll up one row + + PUSH BC ; Save the new cursor row and column numbers. + PUSH HL ; Save the editing area information address. + CALL L1639 ; Shift all edit buffer rows up, and update display file if required. + POP HL ; + POP BC ; + RET C ; Return if edit buffer rows were shifted. + +;The edit buffer rows were not shifted up + + INC HL ; Point to the 'Number of Rows in the Editing Area' value. + LD A,(HL) ; A=Number of rows in the editing area. + CP C ; On the last row of the editing area? + RET Z ; Return with carry flag reset if on the bottom row. + +L2B58: INC C ; Move onto the next row. + SCF ; Signal a new row was moved to. + +L2B5A: RET ; + +; --------------------------------------------------------------------------------------- +; Find Next Screen Line Edit Buffer Editable Position to Left, Wrapping Above if Required +; --------------------------------------------------------------------------------------- +; This routine searches to the left to see if an editable position exists. If there is no editable position +; available to the left on the current row then the previous row is examined from the last column position. +; Entry: B=Column number. +; Carry flag reset. +; Exit : Carry flag set if a position to the 'left' exists. +; B=Number of new editable position. +; HL=Address of new editable position. + +L2B5B: LD D,A ; Save the key code character. + + DEC B ; Back one column position. + JP M,L2B66 ; Jump if already at beginning of row. + + LD E,B ; E=Column number. + CALL L2BDF ; Find Screen Line Edit Buffer editable position to the left, returning column position in B. + LD A,E ; A=Column number. + RET C ; Return if the new column is editable, i.e. the cursor can be moved within this row. + +;Wrap above to the previous row + +L2B66: PUSH DE ; E=Store the column number. + CALL L2B0B ; Move up to the previous row, shifting rows down as appropriate. If moving onto a new BASIC line then + POP DE ; insert the previous BASIC line into the BASIC program if it has been altered. + LD A,E ; A=Column number. + RET NC ; Return if there was no row above. + +;A row above exists + + LD B,#1F ; Column 31. + CALL L2BDF ; Find the last editable column position searching to the left, returning the column number in B. (Returns carry flag set if there is one) + LD A,B ; A=Column number of the closest editable position. + RET C ; Return if an editable position was found, i.e. the cursor can be moved. + +;Return column 0 + + LD A,D ; Restore the key code character. + LD B,#00 ; Set column position 0. + RET ; [*BUG* - This should really ensure the carry flag is reset to signal that no editable position to the left exists, e.g. by using OR A. + ; Fortunately, the carry flag is always reset when this routine is called and so the bug is harmless. Credit: Paul Farrow] + +; ---------------------------------------------------------------------------------------- +; Find Next Screen Line Edit Buffer Editable Position to Right, Wrapping Below if Required +; ---------------------------------------------------------------------------------------- +; This routine searches to the right to see if an editable position exists. If there is no editable position +; available to the right on the current row then the next row is examined from the first column position. +; The routine is also called when a character key has been pressed and in this case if the cursor moves to the next +; row then a blank row is inserted and all affected rows are shifted down. +; Entry: B=Column number. +; C=Row number. +; Exit : Carry flag set if a position to the 'right' exists. +; B=Number of closest editable column, i.e. new column number. +; A=New column position, i.e. preferred column number or indentation column number. +; HL=Address of the new editable position. + +L2B78: LD D,A ; Save the key code character. + + INC B ; Advance to the next column position. + LD A,#1F ; Column 31. + CP B ; + JR C,L2B85 ; Jump if reached end of row. + +;New position is within the row + + LD E,B ; E=New column number. + CALL L2BD4 ; Find Screen Line Edit Buffer editable position from previous column to the right, returning column position in B. + LD A,E ; A=New column number. + RET C ; Return if the new column is editable, i.e. the cursor can be moved within this row. + +;Need to wrap below to the next row + +L2B85: DEC B ; B=Original column position. + PUSH BC ; Save original column and row numbers. + PUSH HL ; HL=Address of the new editable position. + + LD HL,#EC0D ; Editor flags. + BIT 7,(HL) ; Got a key press? + JR NZ,L2BC0 ; Jump if not. + +;A key is being pressed so need to insert a new row + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the current row. + LD A,(HL) ; + BIT 1,A ; Does the BASIC line row span onto another row? + JR NZ,L2BC0 ; Jump if so to test the next row (it could just be the cursor). + +;The BASIC line row does not span onto another row, i.e. cursor at end of line + + SET 1,(HL) ; Signal that the row spans onto another row, i.e. a new blank row containing the cursor. + RES 3,(HL) ; Signal that the row is not the last row of the BASIC line. + + LD HL,#0023 ; Point to the next row. + ADD HL,DE ; + EX DE,HL ; DE=Address of the next row. [Redundant calculation as never used. Could have saved 5 bytes] + + POP HL ; HL=Address of the new editable position. + POP BC ; B=Original column number. C=Row number. + + PUSH AF ; Save flag byte for the previous row. + CALL L2B30 ; Move down to the next row, shifting rows up as appropriate. Returns new row number in C. + POP AF ; Retrieve flag byte for the previous row. + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the new row, as specified in C. + + LD HL,#0023 ; + ADD HL,DE ; HL=Address of the row after the new row. + EX DE,HL ; DE=Address of the row after the new row. HL=Address of the new row. + + RES 0,A ; Signal 'not the start row of the BASIC line'. + SET 3,A ; Signal 'end row of the BASIC line'. + + CALL L2ED3 ; Insert a blank row into the Screen Edit Buffer at row specified by C, shifting rows down. + +; [*BUG* - When typing a line that spills over onto a new row, the new row needs to be indented. However, instead of the newly inserted +; row being indented, it is the row after it that gets indented. The indentation occurs within the Screen Line Edit Buffer and is not +; immediately reflected in the display file. When the newly typed line is executed or inserted into the program area, the Screen Line Edit Buffer +; gets refreshed and hence the effect of the bug is never normally seen. The bug can be fixed by inserting the following instructions. Credit: Paul Farrow. +; +; LD HL,#FFDD ; -35. +; ADD HL,DE +; EX DE,HL ; DE=Points to the start of the previous row.] + + CALL L35F4 ; Indent the row by setting the appropriate number of null characters in the current Screen Line Edit Buffer row. + + LD A,B ; A=First column after indentation. + SCF ; Signal not to produce an error beep. + RET ; + +;Wrap below to the next row. Either a key was not being pressed, or a key was being pressed and the BASIC line spans onto a row below (which could contain the cursor only) + +L2BC0: POP HL ; HL=Address of the new editable position. + POP BC ; B=Original column position. + + PUSH DE ; E=New column number. + CALL L2B30 ; Move down to the next row, shifting rows up as appropriate. If moving onto a new BASIC line then + POP DE ; insert the previous BASIC line into the BASIC program if it has been altered. Returns new row number in C. + LD A,B ; A=Original column position. + RET NC ; Return if there was no row below. + +;A row below exists + + LD B,#00 ; Column 0. + CALL L2BD4 ; Find Screen Line Edit Buffer editable position to the right, returning column position in B. + LD A,B ; A=New column position. + RET C ; Return if an editable position was found, i.e. the cursor can be moved. + +;Return column 0 + + LD A,E ; A=Preferred column number. + LD B,#00 ; Column 0. + RET ; Return with carry flag reset. + +; -------------------------------------------------------------------------------- +; Find Screen Line Edit Buffer Editable Position from Previous Column to the Right +; -------------------------------------------------------------------------------- +; This routine finds the first editable character position in the specified Screen Line Edit Buffer row from the previous column to the right. +; It first checks the current column, then the previous column and then the columns to the right. The column containing the first non-null +; character encountered is returned. +; Entry: B=Column number to start searching from. +; C=Row number. +; Exit : Carry flag set if an editable character was found. +; B=Number of closest editable column. + +L2BD4: PUSH DE ; Save registers. + PUSH HL ; + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + CALL L2E41 ; Find editable position on this row from the previous column to the right, returning column number in B. + + JP L2C65 ; Restore registers and return. [Could have saved a byte by using JR #2C07 (ROM 0)] + +; ---------------------------------------------------------- +; Find Screen Line Edit Buffer Editable Position to the Left +; ---------------------------------------------------------- +; This routine finds the first editable character position in the Screen Line Edit Buffer row from the current column to the left. +; It first checks the current column and returns this if it contains an editable character. Otherwise it searches the columns to +; the left and if an editable character is found then it returns the column to the right of it. +; Entry: B=Column number to start searching from. +; C=Row number. +; Exit : Carry flag set if an editable character was found. +; B=Number of the column after the editable position. + +L2BDF: PUSH DE ; Save registers. + PUSH HL ; + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + CALL L2E63 ; Find editable position from current column to the left, returning the column number in B. + + JP L2C65 ; Restore registers and return. [Could have saved a byte by using JR #2C07 (ROM 0)] + +; ----------------------------------------------------- +; Find Start of Word to Left in Screen Line Edit Buffer +; ----------------------------------------------------- +; This routine searches for the start of the current word to the left within the current Screen Line Edit Buffer. +; It is called from the WORD-LEFT key handler routine. +; Entry: C=Row number. +; Exit : Carry flag set if word to the left is found. +; B=Column position of the found word. + +L2BEA: PUSH DE ; Save registers. + PUSH HL ; + +;Search towards the left of this row until a space or start of line is found + +L2BEC: CALL L2B5B ; Find next Screen Line Edit Buffer editable position to left, moving to next row if necessary. + JR NC,L2C07 ; Jump if not editable, i.e. at start of line. + +L2BF1: CALL L2A1A ; Get character at new position. + CP ' ' ; #20. Is it a space? + JR Z,L2BEC ; Jump back if it is, until a non-space or start of line is found. + +;Search towards the left of this row until the start of the word or start of the line is found + +L2BF8: CALL L2B5B ; Find next Screen Line Edit Buffer editable position to left, moving to next row if necessary. + JR NC,L2C07 ; Jump if not editable, i.e. at start of line. + + CALL L2A1A ; Get character at new position. + CP ' ' ; #20. Is it a space? + JR NZ,L2BF8 ; Jump back if it is not, until a space or start of line is found. + +;A space prior to the word was found + + CALL L2B78 ; Find next Screen Line Edit Buffer editable position to right to start of the word, moving to next row if necessary. + ; [Returns carry flag set since the character will exist] + +L2C07: JR L2C65 ; Jump forward to restore registers and return. + +; ------------------------------------------------------ +; Find Start of Word to Right in Screen Line Edit Buffer +; ------------------------------------------------------ +; This routine searches for the start of the current word to the right within the current Screen Line Edit Buffer. +; It is called from the WORD-RIGHT key handler routine. +; Entry: C=Row number. +; Exit : Carry flag set if word to the right is found. +; B=Column position of the found word. + +L2C09: PUSH DE ; Save registers. + PUSH HL ; + +;Search towards the right of this row until a space or end of line is found + +L2C0B: CALL L2B78 ; Find next Screen Line Edit Buffer editable position to right, moving to next row if necessary. + JR NC,L2C2B ; Jump if none editable, i.e. at end of line. + + CALL L2A1A ; Get character at new position. + CP ' ' ; #20. Is it a space? + JR NZ,L2C0B ; Jump back if it is not, until a space or end of line is found. + +;Search towards the right of this row until the start of a new word or end of the line is found + +L2C17: CALL L2B78 ; Find next Screen Line Edit Buffer editable position to right, moving to next row if necessary. + JR NC,L2C2B ; Jump if none editable, i.e. at end of line. + + CALL L2E41 ; Find editable position on this row from the previous column to the right, returning column number in B. + JR NC,L2C2B ; Jump if none editable, i.e. at start of next line. + + CALL L2A1A ; Get character at new position. + CP ' ' ; #20. Is it a space? + JR Z,L2C17 ; Loop back until a non-space is found, i.e. start of a word. + +;Start of new word found + + SCF ; Indicate cursor position can be moved. + JR L2C65 ; Jump forward to restore registers and return. + +;End of line or start of next line was found + +L2C2B: CALL NC,L2B5B ; If no word on this row then find next Screen Line Edit Buffer editable position to left, + ; moving to previous row if necessary thereby restoring the row number to its original value. + ; [Carry flag is always reset by here so the test on the flag is unnecessary] + OR A ; Clear carry flag to indicate cursor position can not be moved. + JR L2C65 ; Jump forward to restore registers and return. + +; ----------------------------------------------------------- +; Find Start of Current BASIC Line in Screen Line Edit Buffer +; ----------------------------------------------------------- +; This routine searches for the start of the BASIC line, wrapping +; to the previous rows as necessary. +; It is called from the START-OF-LINE key handler routine. +; Entry: C=Row number. +; Exit : Carry flag set if row is not blank. +; B=New cursor column. + +L2C31: PUSH DE ; Save registers. + PUSH HL ; + +L2C33: CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD HL,#0020 ; + ADD HL,DE ; Point to flag byte of next row. + BIT 0,(HL) ; On first row of the BASIC line? + JR NZ,L2C45 ; Jump if on the first row of the BASIC line. + +;Not on the first row of the BASIC line + + CALL L2B0B ; Move up to the previous row, shifting rows down as appropriate. If moving onto a new BASIC line then + ; insert the previous BASIC line into the BASIC program if it has been altered. + JR C,L2C33 ; Jump back if still on the same BASIC line, i.e. was not on first row of the BASIC line. + + JR L2C65 ; Jump forward to restore registers and return. + +;On the first row of the BASIC line, so find the starting column + +L2C45: LD B,#00 ; Column 0. + CALL L2BD4 ; Find Screen Line Edit Buffer editable position to the right, return column position in B. (Returns carry flag reset if blank row) + JR L2C65 ; Jump forward to restore registers and return. + +; --------------------------------------------------------- +; Find End of Current BASIC Line in Screen Line Edit Buffer +; --------------------------------------------------------- +; This routine searches for the end of the BASIC line, wrapping +; to the next rows as necessary. +; It is called from the END-OF-LINE key handler routine. +; Entry: C=Row number. +; Exit : Carry flag set if row is not blank. +; B=New cursor column. + +L2C4C: PUSH DE ; Save registers. + PUSH HL ; + +L2C4E: CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD HL,#0020 ; + ADD HL,DE ; Point to flag byte of next row. + BIT 3,(HL) ; On last row of the BASIC line? + JR NZ,L2C60 ; Jump if on the last row of the BASIC line. + +;Not on the last row of the BASIC line + + CALL L2B30 ; Move down to the next row, shifting rows up as appropriate. If moving onto a new BASIC line then + ; insert the previous BASIC line into the BASIC program if it has been altered. Returns new row number in C. + JR C,L2C4E ; Jump back if still on the same BASIC line, i.e. was not on last row of the BASIC line. + + JR L2C65 ; Jump forward to restore registers and return. + +;On the last row of the BASIC line, so find the last column + +L2C60: LD B,#1F ; Column 31. + CALL L2BDF ; Find the last editable column position searching to the left, returning the column number in B. (Returns carry flag reset if blank row) + +L2C65: POP HL ; Restore registers. + POP DE ; + RET ; + +; ----------------------------------------- +; Insert BASIC Line into Program if Altered +; ----------------------------------------- +; Entry: C=Row number. +; Exit : Carry flag set if BASIC line was not inserted into the program. + +L2C68: LD A,(#EC0D) ; Editor flags. + BIT 3,A ; Has the current line been altered? + SCF ; Signal line not inserted into BASIC program. + RET Z ; Return if it has not. + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD HL,#0020 ; + ADD HL,DE ; HL points to the flag byte for the row. + BIT 3,(HL) ; Is this the end of the BASIC line? + SCF ; Signal line not inserted into BASIC program. + RET Z ; Return if it is not. + + JR L2C8E ; Insert line into BASIC program. + +; ----------------------------------------------------------------------- +; Insert Line into BASIC Program If Altered and the First Row of the Line +; ----------------------------------------------------------------------- +; Entry: C=Row number. +; B=Column number. +; Exit : Carry flag set if successful, reset if a syntax error. + +L2C7C: LD A,(#EC0D) ; Editor flags. + BIT 3,A ; Has current line been altered? + SCF ; Signal success. + RET Z ; Return if it has not. + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + BIT 0,(HL) ; Is this the first row of the BASIC line? + SCF ; Signal success. + RET Z ; Return if it is not. + +; ------------------------------ +; Insert Line into BASIC Program +; ------------------------------ +; This routine parses a line and if valid will insert it into the BASIC program. If in calculator mode +; then the line is not inserted into the BASIC program. If a syntax error is found then the location to +; show the error marker is determined. +; Entry: C=Row number. +; Exit : Carry flag reset if a syntax error. +; Carry flag set if the BASIC line was inserted successfully, and C=Cursor row number, B=Cursor column number, A=Preferred cursor column number. + +L2C8E: LD A,#02 ; Signal on first row of BASIC line. + +;Find the start address of the row in the Screen Line Edit Buffer + +L2C90: CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + BIT 0,(HL) ; First row of the BASIC line? + JR NZ,L2CA3 ; Jump ahead if so. + + DEC C ; Move to previous row. + JP P,L2C90 ; Jump back until found the first row of the BASIC line or the top of the screen. + +;First row of the BASIC line is above the screen + + LD C,#00 ; Row 0. + LD A,#01 ; Signal first row of BASIC line above screen. + +;DE=Start address of the first row of the BASIC line +;HL=Address of the flag byte for the first row of the BASIC line + +L2CA3: LD HL,#EC00 ; BASIC line insertion flags. + LD DE,#EC03 ; BASIC line insertion error flags. + OR #80 ; Signal location of cursor not yet found. + LD (HL),A ; + LD (DE),A ; + + INC HL ; + INC DE ; + LD A,#00 ; [Could have saved 1 byte by using XOR A] + LD (HL),A ; Starting column number of the first visible row of the BASIC line being entered. + LD (DE),A ; + + INC HL ; + INC DE ; + LD A,C ; Fetch the row number of the first visible row of the BASIC line being entered. + LD (HL),A ; Store the start row number of the first visible row of the BASIC line being entered. + LD (DE),A ; + + LD HL,#0000 ; + LD (#EC06),HL ; No editable characters in the line prior to the cursor. + + CALL L335F ; Copy 'Insert Keyword Representation Into Keyword Construction Buffer' routine to RAM. + CALL L3C67 ; Tokenize the typed BASIC line. + + PUSH IX ; IX=Address of cursor settings. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + CALL L026B ; Syntax check/execute the command line. + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + POP IX ; IX=Address of cursor settings. + + LD A,(#5C3A) ; ERR_NR. Fetch error code. + INC A ; Was an error code set? + JR NZ,L2CEF ; Jump ahead if so. + + LD HL,#EC0D ; Editor flags. + RES 3,(HL) ; Signal line has not been altered. + + CALL L365E ; Reset to 'L' Mode. + + LD A,(#EC0E) ; Fetch mode. + CP #04 ; Calculator mode? + CALL NZ,L152F ; If not calculator mode then relist the BASIC program. + + CALL L26FA ; Produce success beep. + CALL L2A07 ; Get current cursor position (C=Row, B=Column, A=Preferred column). + + SCF ; Set the carry flag to signal that that BASIC line was inserted successfully. + RET ; + +; A syntax error occurred +; ----------------------- + +L2CEF: LD HL,#EC00 ; BASIC line insertion flags. + LD DE,#EC03 ; BASIC line insertion error flags. + LD A,(DE) ; Fetch the BASIC line insertion error flags. + RES 7,A ; Signal location of cursor found. + LD (HL),A ; Update the BASIC line insertion flags with the error flags. + + INC HL ; + INC DE ; + LD A,(DE) ; + LD (HL),A ; Restore the initial column number, i.e. column 0. + + INC HL ; + INC DE ; + LD A,(DE) ; + LD (HL),A ; Restore the initial row number, i.e. row number of the first visible row of the BASIC line being entered. + + CALL L3C63 ; Locate the position to insert the error marker into the typed BASIC line. + JR C,L2D0A ; Jump if the error marker was found. + +;Assume the error maker is at the same position as the cursor + + LD BC,(#EC06) ; Fetch the number of editable characters in the line prior to the cursor within the Screen Line Edit Buffer. + +;The position of the error marker within the typed BASIC line has been determined. Now shift the cursor to the corresponding position on the screen. + +L2D0A: LD HL,(#EC06) ; Fetch the number of editable characters in the line prior to the cursor within the Screen Line Edit Buffer. + OR A ; + SBC HL,BC ; HL=Difference between the cursor and the error marker positions (negative if the error marker is after the cursor). + + PUSH AF ; Save the flags. + PUSH HL ; HL=Difference between the cursor and error marker. + CALL L2A07 ; Get current cursor position, returning C=row number, B=column number, A=preferred column number. + POP HL ; HL=Difference between the cursor and error marker. + POP AF ; Restore the flags. + JR C,L2D2A ; Jump if error marker is after the cursor position. + + JR Z,L2D45 ; Jump if cursor is at the same location as the error marker. + +;The error marker is before the cursor position. Move the cursor back until it is at the same position as the error marker. + +L2D1B: PUSH HL ; Save the number of positions to move. + LD A,B ; B=Cursor column number. + CALL L2B5B ; Find previous editable position to the left in the Screen Line Edit Buffer, moving to previous row if necessary. + POP HL ; Retrieve the number of positions to move. + JR NC,L2D45 ; Jump if no previous editable position exists. + + DEC HL ; Decrement the number of positions to move. + LD A,H ; + OR L ; + JR NZ,L2D1B ; Jump back if the cursor position requires further moving. + + JR L2D45 ; Jump ahead to continue. + +;The error marker is after the cursor position. Move the cursor back until it is at the same position as the error marker. + +L2D2A: PUSH HL ; Save the number of positions that the error marker is before the cursor. This will be a + ; negative number is the cursor is after the error marker. + +L2D2B: LD HL,#EC0D ; Editor flags. + RES 7,(HL) ; Signal 'got a key press'. Used in routine at #2B78 (ROM 0) to indicate that a new character has caused the need to shift the cursor position. + + POP HL ; Retrieve the negative difference in the cursor and error marker positions. + EX DE,HL ; DE=Negative difference in the cursor and error marker positions. + + LD HL,#0000 ; Make the negative difference a positive number by subtracting it from 0. + OR A ; + SBC HL,DE ; HL=Positive difference in the cursor and error marker positions. + +L2D38: PUSH HL ; Save the number of positions to move. + LD A,B ; B=Cursor column number. + CALL L2B78 ; Find next editable position to the right in the Screen Line Edit Buffer, moving to next row if necessary. + POP HL ; Retrieve the number of positions to move. + JR NC,L2D45 ; Jump if no next editable position exists. + + DEC HL ; Decrement the number of positions to move. + LD A,H ; + OR L ; + JR NZ,L2D38 ; Jump back if the cursor position requires further moving. + +;The cursor position is at the location of the error marker position + +L2D45: LD HL,#EC0D ; Editor flags. + SET 7,(HL) ; Set 'waiting for key press' flag. + +; [*BUG* - When moving the cursor up or down, an attempt is made to place the cursor at the same column position that it had on the previous row (the preferred column). +; If this is not possible then the cursor is placed at the end of the row. However, it is the intention that the preferred column is still remembered +; and hence an attempt is made to place the cursor at this column whenever it is subsequently moved. However, a bug at this point in the ROM causes +; the preferred column position for the cursor to be overwritten with random data. If the cursor was moved from its original position +; into its error position then the preferred column gets set to zero and the next up or down cursor movement will cause the cursor marker +; to jump to the left-hand side of the screen. However, if the cursor remained in the same position then the preferred column gets set to +; a random value and so on the next up or down cursor movement the cursor marker can jump to a random position on the screen. The bug can +; can reproduced by typing a line that is just longer than one row, pressing enter twice and then cursor down. The cursor marker will probably +; jump somewhere in the middle of the screen. Press an arrow again and the computer may even crash. Credit: Ian Collier (+3), Andrew Owen (128)] + +; [The bug can be fixed by pre-loading the A register with the current preferred column number. Credit: Paul Farrow. +; +; LD A,(#F6F0) ; Fetch the preferred column position.] + + CALL L2A11 ; Store cursor editing position. + + LD A,#17 ; Paper 2, Ink 7 - Red. + CALL L3A96 ; Set the cursor colour to show the position of the error. + + OR A ; Reset the carry flag to signal that a syntax error occurred. + RET ; + +; ---------------------------------------------- +; Fetch Next Character from BASIC Line to Insert +; ---------------------------------------------- +; This routine fetches a character from the BASIC line being inserted. The line may span above or below the screen, and so the character +; is retrieved from the appropriate buffer. +; Exit : A=Character fetched from the current position, or 'Enter' if end of line found. + +L2D54: LD HL,#EC00 ; Point to the 'insert BASIC line' details. + BIT 7,(HL) ; Has the column with the cursor been found? + JR Z,L2D62 ; Jump if it has been found. + + LD HL,(#EC06) ; + INC HL ; Increment the count of the number of editable characters in the BASIC line up to the cursor. + LD (#EC06),HL ; + +L2D62: LD HL,#EC00 ; Point to the 'insert BASIC line' details. + LD A,(HL) ; Fetch flags. + INC HL ; + LD B,(HL) ; Fetch the column number of the character being examined. + INC HL ; + LD C,(HL) ; Fetch the row number of the character being examined. + PUSH HL ; + AND #0F ; Extract the status code. + +;Register A: +; Bit 0: 1=First row of the BASIC line off top of screen. +; Bit 1: 1=On first row of the BASIC line. +; Bit 2: 1=Using lower screen and only first row of the BASIC line visible. +; Bit 3: 1=At end of last row of the BASIC line (always 0 at this point). + + LD HL,L2D85 ; Jump table to select appropriate handling routine. + CALL L3FCE ; Call handler routine. + +;Register L: +; #01 - A character was returned from the Above-Screen Line Edit Buffer row. +; #02 - A character was returned from the Screen Line Edit Buffer row. +; #04 - A character was returned from the Below-Screen Line Edit Buffer row. +; #08 - At the end of the last row of the BASIC line. +;Register A holds the character fetched or 'Enter' if at the end of the BASIC line. + + LD E,L ; E=Return status. + POP HL ; + JR Z,L2D79 ; Jump if no match found. + + LD A,#0D ; A='Enter' character. + +L2D79: LD (HL),C ; Save the next character position row to examine. + DEC HL ; + LD (HL),B ; Save the next character position column to examine. + DEC HL ; + PUSH AF ; Save the character. + LD A,(HL) ; Fetch the current status flags. + AND #F0 ; Keep the upper nibble. + OR E ; Update the location flags that indicate where to obtain the next character from. + LD (HL),A ; Store the status flags. + POP AF ; Retrieve the character. + RET ; + +; ------------------------------- +; Fetch Next Character Jump Table +; ------------------------------- +; Jump to one of three handling routines when fetching the next character from the BASIC line to insert. + +L2D85: DEFB #03 ; Number of table entries. + DEFB #02 ; On first row of the BASIC line. + DEFW L2DAC ; + DEFB #04 ; Using lower screen and only first row of the BASIC line visible. + DEFW L2DE9 ; + DEFB #01 ; First row of the BASIC line off top of screen. + DEFW L2D8F ; + +; ------------------------------------------------------------------------------------- +; Fetch Character from the Current Row of the BASIC Line in the Screen Line Edit Buffer +; ------------------------------------------------------------------------------------- +; Fetch character from the current row of the BASIC line in the Screen Line Edit Buffer, skipping nulls until the end of +; the BASIC line is found. +; Entry: C=Row number. +; Exit : L=#01 - A character was returned from the Above-Screen Line Edit Buffer row, with A holding the character. +; #02 - A character was returned from the Screen Line Edit Buffer row, with A holding the character. +; #04 - A character was returned from the Below-Screen Line Edit Buffer row, with A holding the character. +; #08 - At the end of the last row of the BASIC line, with A holding an 'Enter' character. +; Zero flag set to indicate a match from the handler table was found. + +;Table entry point - First row of BASIC line off top of screen + +L2D8F: CALL L32B7 ; Find row address in Above-Screen Line Edit Buffer, return in DE. + +L2D92: CALL L2E0E ; Fetch character from Above-Screen Line Edit Buffer row. + JR NC,L2D9E ; Jump if end of row reached. + + CP #00 ; Is it a null character, i.e. not editable? + JR Z,L2D92 ; Jump back if so until character found or end of row reached. + + LD L,#01 ; Signal a character was returned from the Above-Screen Line Edit Buffer row, with A holding the character. + RET ; Return with zero flag reset to indicate match found. + +;End of row reached - no more editable characters in Above-Screen Line Edit Buffer row + +L2D9E: INC C ; Next row. + LD B,#00 ; Column 0. + LD HL,(#F9DB) ; [*BUG* - This should be LD HL,#F9DB. The bug manifests itself when Enter is pressed on an edited BASIC line that goes off + ; the top of the screen and causes corruption to that line. The bug at #30D0 (ROM 0) that sets default data for the Below-Screen Line Edit Buffer + ; implies that originally there was the intention to have a pointer into the next location to use within that buffer, and so it seems to + ; reasonable to assume the same arrangement would have been intended for the Above-Screen Line Edit Buffer. If that were the case then the + ; instruction here was intended to fetch the next address within the Above-Screen Line Edit Buffer. Credit: Ian Collier (+3), Andrew Owen (128)] + + LD A,C ; Fetch the row number. + CP (HL) ; Exceeded last row of Above-Screen Line Edit Buffer? + JR C,L2D8F ; Jump back if not exceeded last row the Above-Screen Line Edit Buffer. + +;All characters from rows off top of screen fetched so continue onto the rows on screen + +; [Note it is not possible to have more than 20 rows off the top of the screen] + + LD B,#00 ; Column 0. + LD C,#00 ; Row 0. This is the first visible row of the BASIC line on screen. + +; Table entry point - On visible row of BASIC line +; ------------------------------------------------ + +;C=Row number of the first visible row of the BASIC line in the Screen Line Edit Buffer +;B=Starting column number of the first visible row of the BASIC line in the Screen Line Edit Buffer + +L2DAC: PUSH HL ; Save address of the table entry. + + LD HL,#F6EE ; Point to the cursor position details. + LD A,(HL) ; Fetch the row number of the cursor. + CP C ; Is cursor on the first visible row of the BASIC line? + JR NZ,L2DBE ; Jump if not. + +;Cursor on first visible row of the BASIC line in the Screen Line Edit Buffer. + + INC HL ; + LD A,(HL) ; Fetch the column number of the cursor. + CP B ; Reached the column with the cursor in the first visible row of the BASIC line? + JR NZ,L2DBE ; Jump if not. + + LD HL,#EC00 ; BASIC line insertion flags. + RES 7,(HL) ; Indicate that the column with the cursor has been found. + +L2DBE: POP HL ; Retrieve address of the table entry. + +L2DBF: CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + CALL L2E0E ; Fetch character from Screen Line Edit Buffer row at column held in B, then increment B. + JR NC,L2DCE ; Jump if end of row reached. + + CP #00 ; Is the character a null, i.e. not editable? + JR Z,L2DAC ; Jump back if null to keep fetching characters until a character is found or the end of the row is reached. + +;A character in the current row of the BASIC line was found + + LD L,#02 ; L=Signal a character was returned from the Screen Line Edit Buffer row, with A holding the character. + RET ; Return with zero flag reset to indicate match found. + +;End of row reached - no editable characters in the Screen Line Edit Buffer row + +L2DCE: LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + BIT 3,(HL) ; Is it the last row of the BASIC line? + JR Z,L2DDB ; Jump if not. + +;On last row of the BASIC line and finished fetching characters from the line + + LD L,#08 ; L=Signal at the end of the last row of the BASIC line. + LD A,#0D ; A='Enter' character. + RET ; Return with zero flag reset to indicate match found. + +;Not on the last row of the BASIC line so move to the beginning of the next, if it is on screen. + +L2DDB: LD HL,#F6F3 ; Point to the 'top row scroll threshold' value. + INC C ; Next row of the BASIC line in the Screen Line Edit Buffer. + LD A,(HL) ; Fetch the number of the last row in the Screen Line Edit Buffer. + CP C ; Exceeded the upper scroll threshold? + LD B,#00 ; Column 0. + JR NC,L2DBF ; Jump back if not to retrieve the character from the next row. + +;The upper row threshold for triggering scrolling the screen has been reached so proceed to scroll up one line + + LD B,#00 ; Column 0. [Redundant byte] + LD C,#01 ; Row 1. (Row 0 holds a copy of the last row visible on screen) + +; Table entry point - Using lower screen and only top row of a multi-row BASIC line is visible +; -------------------------------------------------------------------------------------------- + +L2DE9: CALL L31C3 ; Find the address of the row specified by C in Below-Screen Line Edit Buffer, into DE. + +L2DEC: CALL L2E0E ; Fetch character from Below-Screen Line Edit Buffer row, incrementing the column number. + JR NC,L2DF8 ; Jump if end of row reached. + + CP #00 ; Is the character a null, i.e. not editable? + JR Z,L2DEC ; Jump back if null to keep fetching characters until a character is found or the end of the row is reached. + + LD L,#04 ; L=Signal a character was returned from the Below-Screen Line Edit Buffer row, with A holding the character. + RET ; Return with zero flag reset to indicate match found. + +;End of row reached - no editable characters in the (below screen) Below-Screen Line Edit Buffer row + +L2DF8: LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + BIT 3,(HL) ; Is it the last row of the BASIC line? + JR NZ,L2E09 ; Jump if so. + + INC C ; Next row. + LD B,#00 ; Column 0. + + LD A,(#F6F5) ; Fetch number of rows in the Below-Screen Line Edit Buffer. + CP C ; Exceeded last line in Below-Screen Line Edit Buffer? + JR NC,L2DE9 ; Jump back if not to retrieve the character from the next row. + +;All characters from rows off bottom of screen fetched so return an 'Enter' + +; [Note it is not possible to have more than 20 rows off the bottom of the screen] + +L2E09: LD L,#08 ; L=Signal at the end of the last row of the BASIC line. + LD A,#0D ; A='Enter' character. + RET ; Return with zero flag reset to indicate match found. + +; ------------------------------------ +; Fetch Character from Edit Buffer Row +; ------------------------------------ +; Entry: B =Column number. +; DE=Start address of row in Edit Buffer. +; Exit : Carry flag set indicates character fetched, reset if column out of range. + +L2E0E: LD A,#1F ; Column 31. + CP B ; Is column + CCF ; + RET NC ; Return if B is greater than 31. + + LD L,B ; + LD H,#00 ; HL=Column number. + ADD HL,DE ; + LD A,(HL) ; Fetch the character at the specified column. + INC B ; Increment the column number. + SCF ; Signal character fetched. + RET ; + +; ----------------------- +; Upper Screen Rows Table +; ----------------------- +; Copied to #EC15-#EC16. + +L2E1B: DEFB #01 ; Number of bytes to copy. + DEFB #14 ; Number of editing rows (20 for upper screen). + +; ----------------------- +; Lower Screen Rows Table +; ----------------------- +; Copied to #EC15-#EC16. + +L2E1D: DEFB #01 ; Number of bytes to copy. + DEFB #01 ; Number of editing rows (1 for lower screen). + +; -------------------- +; Reset to Main Screen +; -------------------- + +L2E1F: LD HL,#5C3C ; TVFLAG. + RES 0,(HL) ; Signal using main screen. + LD HL,L2E1B ; Upper screen lines table. + LD DE,#EC15 ; Destination workspace variable. The number of editing rows on screen. + JP L3FBA ; Copy one byte from #2E1C (ROM 0) to #EC15 + +; --------------------- +; Reset to Lower Screen +; --------------------- + +L2E2D: LD HL,#5C3C ; TVFLAG. + SET 0,(HL) ; Signal using lower screen. + + LD BC,#0000 ; + CALL L372B ; Perform 'PRINT AT 0,0;'. + + LD HL,L2E1D ; Lower screen lines table. + LD DE,#EC15 ; Destination workspace variable. The number of editing rows on screen. + JP L3FBA ; Copy one byte from #2E1E (ROM 0) to #EC15 + +; -------------------------------------------------------------------- +; Find Edit Buffer Editable Position from Previous Column to the Right +; -------------------------------------------------------------------- +; This routine finds the first editable character position in the specified edit buffer row from the previous column to the right. +; It first checks the current column, then the previous column and then the columns to the right. The column containing the first +; non-null character encountered is returned. +; Entry: B =Column number to start searching from. +; DE=Start of row in edit buffer. +; Exit : Carry flag set if an editable character was found. +; HL=Address of closest editable position. +; B =Number of closest editable column. + +L2E41: LD H,#00 ; [Could have saved 1 byte by calling routine at #2E7B (ROM 0)] + LD L,B ; HL=Column number. + ADD HL,DE ; HL=Address in edit buffer of the specified column. + + LD A,(HL) ; Fetch the contents. + CP #00 ; Is it a null character, i.e. end-of-line or past the end-of-line? + SCF ; + RET NZ ; Return if this character is part of the edited line. + + LD A,B ; + OR A ; + JR Z,L2E5B ; Jump ahead if the first column. + + PUSH HL ; Otherwise check the + DEC HL ; preceding byte + LD A,(HL) ; and if it is non-zero + CP #00 ; then return with + SCF ; HL pointing to the + POP HL ; first zero byte. + RET NZ ; + +L2E56: LD A,(HL) ; Get the current character. + CP #00 ; Is it a null (i.e. end-of-line)? + SCF ; Signal position is editable. + RET NZ ; Return if this character is part of the edited line. + +L2E5B: INC HL ; Advance to the next position. + INC B ; Increment the column number. + LD A,B ; + CP #1F ; Reached the end of the row? + JR C,L2E56 ; Jump back if more columns to check. + + RET ; Return with carry flag reset if specified column position does not exist. + +; ---------------------------------------------- +; Find Edit Buffer Editable Position to the Left +; ---------------------------------------------- +; This routine finds the first editable character position in the specified edit buffer row from the current column to the left. +; It first checks the current column and returns this if it contains an editable character. Otherwise it searches the columns to +; the left and if an editable character is found then it returns the column to the right of it. +; Entry: B =Column number to start searching from. +; DE=Start of row in edit buffer. +; Exit : Carry flag set if an editable character was found. +; HL=Address of closest editable position. +; B =Number of the column after the editable position. + +L2E63: LD H,#00 ; [Could have saved 1 byte by calling routine at #2E7B (ROM 0)] + LD L,B ; HL=Column number. + ADD HL,DE ; HL=Address in edit buffer of the specified column. + + LD A,(HL) ; Fetch the contents. + CP #00 ; Is it a null character, i.e. end-of-line or past the end-of-line? + SCF ; Signal position is editable. + RET NZ ; Return if an editable character was found. + +L2E6C: LD A,(HL) ; Get the current character. + CP #00 ; Is it a null, i.e. non-editable? + JR NZ,L2E78 ; Jump if not. + + LD A,B ; At column 0? + OR A ; + RET Z ; Return if so. + + DEC HL ; Next column position to test. + DEC B ; Decrement column index number. + JR L2E6C ; Repeat test on previous column. + +L2E78: INC B ; Advance to the column after the editable position. + SCF ; Signal position is editable. + RET ; + +; ------------------------------- +; Fetch Edit Buffer Row Character +; ------------------------------- +; Entry: DE=Add of edit buffer row. +; B =Column number. +; Exit : A =Character at specified column. +; +; [Not used by the ROM] + +L2E7B: LD H,#00 ; + LD L,B ; HL=Column number. + ADD HL,DE ; HL=Address in edit buffer of the specified column. + LD A,(HL) ; Get the current character. + RET ; + +; --------------------------------------------- +; Insert Character into Screen Line Edit Buffer +; --------------------------------------------- +; Called when a non-action key is pressed. It inserts a character into the Screen Line Edit Buffer if there is room. +; Entry: A=Character code. +; B=Cursor column position. +; C=Cursor row position. + +L2E81: LD HL,#EC0D ; Editor flags. + OR A ; Clear carry flag. [Redundant since carry flag return state never checked] + BIT 0,(HL) ; Is the Screen Line Edit Buffer is full? + RET NZ ; Return if it is. + + PUSH BC ; Save cursor position. + PUSH AF ; Save key code. [Redundant since #30B4 (ROM 0) preserves AF] + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + + POP AF ; Get key code. [Redundant since #30B4 (ROM 0) preserves AF] + +;Insert the character into the current row. If a spill from this row occurs then insert that character into the start of the +;following row and shift all existing characters right by one. Repeat this process until all rows have been shifted. + +L2E8E: CALL L16AC ; Insert character into edit buffer row at current cursor position, shifting the row right. Returns carry flag reset. + ; Zero flag will be set if byte shift out of last column position was #00. + + PUSH AF ; Save key code and flags. + + EX DE,HL ; HL=Address of edit buffer row. DE=Address of flags. + CALL L3604 ; Print a row of the edit buffer to the screen. + EX DE,HL ; DE=Address of edit buffer row. HL=Address of flags. + + POP AF ; Get key code and flags. + CCF ; Sets the carry flag since it was reset via the call to #16AC (ROM 0). [Redundant since never tested] + JR Z,L2ECC ; Jump ahead to make a return if there was no spill out from column 31, with the carry flag set. + +;There was a spill out from the current row, and so this character will need to be inserted as the first character of the following row. +;If this is the last row of the BASIC line then a new row will need to be inserted. + + PUSH AF ; Save key code. + + LD B,#00 ; First column in the next row. + INC C ; Next row. + + LD A,(#EC15) ; The number of editing rows on screen. + CP C ; Has the bottom of the Screen Line Edit Buffer been reached? + JR C,L2EC8 ; Jump ahead if so. + +;The editing screen is not full + + LD A,(HL) ; Fetch contents of flag byte for the row (byte after the 32 columns). + LD E,A ; E=Old flags. + AND #D7 ; Mask off 'last row of BASIC line' flag. [Other bits not used, could have used AND #F7] + CP (HL) ; Has the status changed? + LD (HL),A ; Store the new flags, marking it as not the last BASIC row. + LD A,E ; A=Original flags byte for the row. + SET 1,(HL) ; Signal that the row spans onto another row. + + PUSH AF ; Save the flags. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the following row, as specified in C. + POP AF ; Fetch the flags. + JR Z,L2EC2 ; Jump if the character was not inserted into the last row of the BASIC line. + +;The character was inserted into the last row of the BASIC line causing a spill of an existing character into a new row, +;and therefore a new 'last' row needs to be inserted. + + RES 0,A ; Signal not the first row of the BASIC line. + CALL L2ED3 ; Insert a blank line into the Screen Edit Buffer. + JR NC,L2ECC ; Jump if the buffer is full to exit. + + CALL L35F4 ; Indent the row by setting the appropriate number of null characters in the current Screen Line Edit Buffer row. + + POP AF ; Get key code. + JR L2E8E ; Jump back to insert the character in the newly inserted row. [Could have saved 2 bytes by using JR #2EC5 (ROM 0)] + +;The character was not inserted into the last row of the BASIC line, so find the first editable position on the following row, i.e. +;skip over any indentation. + +L2EC2: CALL L2E41 ; Find editable position on this row from the previous column to the right, returning column number in B. + POP AF ; Get key code. + JR L2E8E ; Jump back to insert the character into the first editable position of next the row. + +;The Screen Edit Line Buffer is full and the character insertion requires shifting of all rows that are off screen in the Below-Screen Line Edit Buffer. + +L2EC8: POP AF ; Get key code. + CALL L316E ; Insert the character at the start of the Below-Screen Line Edit Buffer, shifting all existing characters to the right. + +;All paths join here + +L2ECC: POP BC ; Retrieve cursor position. + RET ; + +; ------------------------------------------------------------ +; Insert Blank Row into Screen Edit Buffer, Shifting Rows Down +; ------------------------------------------------------------ +; This routine inserts a blank row at the specified row, shifting affected rows down. +; Entry: C=Row number to insert the row at. +; Exit : Carry flag set to indicate edit buffer rows were shifted. + +L2ECE: CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD A,#09 ; Signal 'first row' and 'last row', indicating a new blank row. + +;DE=Address of row within Screen Line Edit Buffer. +;C=Row number to insert the row at. +;A=Screen Line Edit Buffer row flags value. + +L2ED3: PUSH BC ; Save registers. + PUSH DE ; + + LD B,C ; B=Row number. + LD HL,L2EEF ; The empty row data. + LD C,A ; C=Flags for the row. + + PUSH BC ; + CALL L1675 ; Shift all Screen Line Edit Buffer rows down and insert a new blank row, updating the display file if required. + POP BC ; + LD A,C ; A=Flags for the row. + JR NC,L2EEC ; Jump if no edit buffer rows were shifted. + +;Rows were shifted down + + LD C,B ; B=Row number, where the new blank row now is. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + + LD HL,#0020 ; Point to the flag byte for the row. + ADD HL,DE ; + LD (HL),A ; Store the flag byte value for the row. + SCF ; Signal edit buffer rows were shifted. + +L2EEC: POP DE ; Restore registers. + POP BC ; + RET ; + +; -------------------------- +; Empty Edit Buffer Row Data +; -------------------------- + +L2EEF: DEFB #00 ; 32 null column markers, i.e. none of the columns are editable. + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + DEFB #00 + + DEFB #09 ; Flags: + ; Bit 0: 1=The first row of the BASIC line. + ; Bit 1: 0=Does not span onto another row. + ; Bit 2: 0=Not used (always 0). + ; Bit 3: 1=The last row of the BASIC line. + ; Bit 4: 0=No associated line number. + ; Bit 5: 0=Not used (always 0). + ; Bit 6: 0=Not used (always 0). + ; Bit 7: 0=Not used (always 0). + DEFW #0000 ; There is no BASIC line number associated with this edit row. + +; ------------------------------------------------------------------- +; Delete a Character from a BASIC Line in the Screen Line Edit Buffer +; ------------------------------------------------------------------- +; Delete a character at the specified position, shifting subsequent characters left as applicable. +; Entry: B=Column number. +; C=Row number. + +L2F12: PUSH BC ; Save initial cursor row and column numbers. + + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + + PUSH BC ; Stack initial cursor row and column numbers again. + +;Enter a loop to find the last row of the BASIC line or the end of the visible screen, whichever comes first + +L2F17: LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for this row. + BIT 1,(HL) ; Does the row span onto another row? + LD A,#00 ; A null character will be inserted. [Could have saved 1 byte by using XOR A and placing it above the BIT 1,(HL) instruction] + JR Z,L2F31 ; Jump ahead if the row does not span onto another row, i.e. the last row. + +;The row spans onto another + + INC C ; C=Advance to the next row. + LD HL,#0023 ; + ADD HL,DE ; + EX DE,HL ; DE points to the first character of the next row. HL points to the first character of the current row. + + LD A,(#EC15) ; A=Number of editing lines. + CP C ; Has the end of the screen been reached? + JR NC,L2F17 ; Jump back if within screen range to find the last row of the BASIC line. + +;The end of the screen has been reached without the end of the BASIC line having been reached + + DEC C ; Point to last row on screen. + CALL L31C9 ; Shift all characters of the BASIC Line held within the Below-Screen Line Edit Buffer. + +;A loop is entered to shift all characters to the left, beginning with the last row of the BASIC line in the Screen Line Edit Buffer +;and until the row that matches the current cursor position is reached. + +L2F31: POP HL ; Fetch the initial cursor row and column numbers. + +L2F32: PUSH HL ; Stack initial cursor row and column numbers. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the last row, as specified in C. + POP HL ; HL=Initial cursor row and column numbers. + + LD B,A ; B=Character to insert. + LD A,C ; A=Row number to delete from. + CP L ; Deleting from the same row as the cursor is on within the BASIC line? + LD A,B ; A=Character to insert. + PUSH AF ; Save the flags status. + JR NZ,L2F41 ; Jump if not deleting from the row containing the cursor. + +;Deleting from the row matching the cursor position within the BASIC line, therefore only shift those bytes after the cursor position + + LD B,H ; B=Initial column number. + JR L2F4A ; Jump ahead to continue, with zero flag set to indicate deleting from the row contain the cursor. + +;Deleting on row after that matching the cursor position, therefore shift all editable characters within the row + +L2F41: PUSH AF ; Save the character to insert. + PUSH HL ; Save initial cursor row and column numbers. + + LD B,#00 ; + CALL L2E41 ; Find first editable position on this row searching to the right, returning column number in B. + + POP HL ; HL=Initial cursor row and column numbers. + POP AF ; A=Character to insert, and zero flag reset to indicate not deleting from the row contain the cursor. + +;DE=Start address of Screen Line Edit Buffer row. +;A=Character to shift into right of row. +;B=The column to start shifting at. +;C=Row number to start shifting from. +;Zero flag is set if deleting from the row matching the cursor position. + +L2F4A: PUSH HL ; HL=Initial cursor row and column numbers. + + LD HL,#F6F4 ; Deleting flags. + SET 0,(HL) ; Signal deleting on the row matching the cursor position. + JR Z,L2F54 ; Jump if deleting from the row matching the cursor position. + + RES 0,(HL) ; Signal not deleting on the row matching the cursor position. + +L2F54: CALL L16C1 ; Insert the character into the end of the edit buffer row, shifting all columns left until the cursor position is reached. + + PUSH AF ; A=Character shifted out, and therefore to be potentially shifted into the end of the previous row. + PUSH BC ; B=New column number. C=Row number. + PUSH DE ; DE=Start address of row to delete from. + + LD HL,#F6F4 ; Deleting flags. + BIT 0,(HL) ; Deleting from the row matching the cursor position? + JR NZ,L2F6F ; Jump ahead if so. + +;Deleting from a row after the cursor position + + LD B,#00 ; Column 0. + CALL L2BD4 ; Is there an editable character on the row? + JR C,L2F6F ; Jump if there is. + +;Shifting the characters on this row has resulted in a blank row, so shift all rows below screen up to remove this blank row + + CALL L2F80 ; Shift up all BASIC line rows below to close the gap. + + POP DE ; DE=Start address of row to delete from. + POP BC ; B=New column number. C=Row number. + JR L2F74 ; Jump ahead. + +;There are characters remaining on the row following the shift so display this to the screen and then continue to shift the remaining rows + +L2F6F: POP HL ; HL=Start address of the row. + POP BC ; B=New column number. C=Row number. + + CALL L3604 ; Print the row of the edit buffer to the screen, if required. + +L2F74: POP AF ; A=Character to insert. + + DEC C ; Previous row. + LD B,A ; B=Character to insert. + + POP HL ; HL=Initial cursor row and column numbers. + POP AF ; Retrieve the flags status (zero flag set if deleting from the row matching the cursor position). + LD A,B ; A=Character to insert. + JP NZ,L2F32 ; Jump back if not deleting from the row matching the cursor position, i.e. all rows after the cursor have not yet been shifted. + +; [*BUG* - The 'line altered' flag is not cleared when an 'edited' null line is entered. To reproduce the bug, insert a couple of BASIC lines, type a character, +; delete it, and then cursor up or down onto a program line. The line is considered to have been changed and so is processed as if it consists +; of characters. Further, when cursor down is pressed to move to a BASIC line below, that line is deemed to have changed and hence moving off from it +; causing that line to be re-inserted into the BASIC program. Credit: Ian Collier (+3), Paul Farrow (128)] + +; [The fix for the bug is to check whether all characters have been deleted from the line and if so to reset the 'line altered' flag. +; This would require the following code to be inserted at this point. Credit: Paul Farrow. +; +; PUSH DE ; +; LD HL,#0020 ; +; ADD HL,DE ; Point to the flag byte for this row. +; POP DE +; BIT 0,(HL) ; First row of BASIC line in addition to the last? +; JR Z,SKIP_CLEAR ; Jump ahead if not. +; +; LD B,#00 ; +; CALL #2E41 (ROM 0) ; Is this a blank row? i.e. Find editable position on this row to the right, returning column number in B. +; JR C,SKIP_CLEAR ; Jump if a character exists on the line. +; +; LD HL,#EC0D +; RES 3,(HL) ; Signal that the current line has not been altered. +; +;SKIP_CLEAR: +; XOR A ; Set the preferred column to 0.] + + SCF ; [Redundant since never subsequently checked] + POP BC ; Retrieve initial cursor row and column numbers. + RET ; + +; ----------------------------------------------------------- +; Shift Rows Up to Close Blank Row in Screen Line Edit Buffer +; ----------------------------------------------------------- +; The cursor is on a blank row but has been moved off of it. Therefore shift all BASIC lines below it up so as to remove the blank row. +; Entry: DE=Address of the row in the Screen Line Edit Buffer containing the cursor. +; C =Row number in the Screen Line Edit Buffer containing the cursor. +; Carry flag set if rows were shifted up, i.e. a row below existed. + +L2F80: LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + LD A,(HL) ; + BIT 0,(HL) ; Is the cursor on a blank row (which is flagged as the first row of a BASIC line)? + JR NZ,L2FB2 ; Jump ahead if it is. [Could have improved speed by jumping to #2FB6 (ROM 0) since DE already holds the start address of the row] + +;Cursor not on a blank row but is on its own row at the end of a multi-row BASIC line + + PUSH AF ; Save the cursor row flag byte. + PUSH BC ; Save the cursor row number in C. + + LD A,C ; Is the cursor on row 0? + OR A ; + JR NZ,L2FA4 ; Jump ahead if it is not, i.e. there is at least one row above. + +;Cursor on row 0, hence a BASIC line must be off the top of the screen [???? Can this ever be the case?] + + PUSH BC ; Save the cursor row number. + + LD HL,(#FC9A) ; Line number at top of screen. + CALL L334A ; Find closest line number (or #0000 if no line). + LD (#FC9A),HL ; Line number at top of screen. + + LD A,(#F9DB) ; Fetch the number of rows of the BASIC line that are in the Above-Screen Line Edit Buffer, + LD C,A ; i.e. that are off the top of the screen. + DEC C ; Decrement the row count, i.e. one less row off the top of the screen. + CALL L32B7 ; DE=Address of row in Above-Screen Line Edit Buffer. + + POP BC ; Retrieve the cursor row number. + JR L2FA8 ; Jump ahead. + +;There is a row above so set this as the last row of the BASIC line + +L2FA4: DEC C ; Previous row, i.e. the last row of the BASIC line that contains editable characters. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the previous row. + +L2FA8: POP BC ; Retrieve the cursor row number. + POP AF ; Retrieve the cursor row flag byte, which indicates last row of BASIC line. + + LD HL,#0020 ; Point to the flag byte for the previous row. + ADD HL,DE ; + RES 1,(HL) ; Signal that the previous row does not span onto another row. + OR (HL) ; Keep the previous row's first BASIC row flag. + LD (HL),A ; Update the flag byte for the previous row. + +;Shift up all rows below the old cursor position within the Screen Line Edit Buffer and including the Below-Screen Line Edit Buffer, and update the display file if required + +L2FB2: LD B,C ; B=Row number in the Screen Line Edit Buffer. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + CALL L30DF ; Shift up rows of the BASIC line in the Below-Screen Line Edit Buffer, or insert the next line BASIC line if buffer empty. + JP L1648 ; Shift Screen Line Edit Buffer rows up from row specified by B and update the display file if required. + ; [Could have saved 3 bytes by replacing the instructions CALL #30DF (ROM 0) / JP #1648 (ROM 0) with JP #1645 (ROM 0)] + +; ------------------------------------ +; DELETE-WORD-LEFT Key Handler Routine +; ------------------------------------ +; This routine deletes to the start of the current word that the cursor is on, or if it is on the first +; character of a word then it deletes to the start of the previous word. Since the function works by deleting one +; character at a time, display file updates are disabled whilst the function is executing to prevent screen flicker. +; If there is no word to delete then an error beep is requested. +; +; Symbol: <-- DEL +; <-- +; +; Exit: Carry flag reset to indicate to produce an error beep and set not to produce an error beep. + +L2FBC: CALL L3084 ; Remove cursor attribute, disable display file updates and get current cursor position. Exits with HL pointing to the editing area information. + +L2FBF: PUSH HL ; Save address of the editing area information. + CALL L3095 ; Does a previous character exist in the current Screen Line Edit Buffer row? + JR Z,L2FF7 ; Jump if at the start of the BASIC line to print all rows. + + CALL L2B5B ; Is previous column position editable? (Returns carry flag set if editable) + POP HL ; Retrieve address of the editing area information. + JR NC,L2FF8 ; Jump if not editable to print all rows. + +;A previous character exists and is editable + + CALL L2A1A ; Get character from current cursor position. + PUSH AF ; Save current character. + PUSH HL ; Save address of the editing area information. + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + POP HL ; Retrieve address of the editing area information. + POP AF ; Retrieve current character. + CP #20 ; Is it a space? + JR Z,L2FBF ; Jump back if so to find the end of the last word. + +;The end of the word to delete has been found, so enter a loop to search for the start of the word + +L2FD9: PUSH HL ; Save address of the editing area information. + CALL L3095 ; Does a previous character exist in the current Screen Line Edit Buffer row? + JR Z,L2FF7 ; Jump if at the start of a BASIC line to print all rows. + + CALL L2B5B ; Is previous column position editable? (Returns carry flag set if editable) + POP HL ; Retrieve address of the editing area information. + JR NC,L2FF8 ; Jump if not editable to print all rows. + + CALL L2A1A ; Get character from current cursor position + CP #20 ; Is it a space? + JR Z,L2FF3 ; Jump if so. + +;Character is not a space + + PUSH HL ; Save address of the editing area information. + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + POP HL ; Retrieve address of the editing area information. + JR L2FD9 ; Jump back to delete next character until start of the word found. + +;A space prior to a word has been found + +L2FF3: PUSH HL ; Save address of the editing area information. + CALL L2B78 ; Find next Screen Line Edit Buffer editable position to right, moving to next row if necessary. + +L2FF7: POP HL ; Retrieve address of the editing area information. + +;Print all rows to the screen + +L2FF8: LD A,B ; Fetch the new end column number. + + PUSH AF ; Save the flags status. + PUSH HL ; Save address of the editing area information. + + LD HL,#EEF5 ; + RES 2,(HL) ; Re-enable display file updates. + + LD A,(#EC15) ; The number of editing rows on screen. [This will end up being used as the alternate cursor column] + + PUSH BC ; Save the row and new column numbers. + LD B,#00 ; B=Print from row 0. + LD C,A ; C=Number of editing rows on screen. + CP A ; Set the zero flag to signal not to change cursor position settings. + CALL L1605 ; Print all Screen Line Edit Buffer rows to the display file. + POP BC ; Retrieve the row and new column numbers. + + LD HL,#EC0D ; Editor flags. + SET 3,(HL) ; Indicate current line has been altered. + POP HL ; Retrieve address of the editing area information. + +; [*BUG* - The preferred cursor column field gets corrupted with the number of editing rows on screen. Credit: Ian Collier (+3), Andrew Owen (128)] + +; [The bug can be fixed by pre-loading the A register with the current preferred column number. Credit: Paul Farrow. +; +; LD A,(#F6F0) ; Fetch the preferred column position.] + + CALL L29F8 ; Store editing position and print cursor. + + POP AF ; Retrieve the flags status. + RET ; + +; ------------------------------------- +; DELETE-WORD-RIGHT Key Handler Routine +; ------------------------------------- +; This routine deletes to the start of the next word. Since the function works by deleting one character +; at a time, display file updates are disabled whilst the function is executing to prevent screen flicker. +; If there is no word to delete then an error beep is requested. +; +; Symbol: --> DEL +; --> +; +; Exit: Carry flag set to indicate not to produce an error beep. + +L3017: CALL L3084 ; Remove cursor attribute, disable display file updates and get current cursor position. Exits with HL pointing to the editing area information. + +L301A: PUSH HL ; Save address of the editing area information. + CALL L2A1A ; Get character from current cursor position. + POP HL ; Retrieve address of the editing area information. + CP #00 ; Is it a null character, i.e. end of BASIC line? + SCF ; Signal do not produce an error beep. + JR Z,L2FF8 ; Jump if end of the BASIC line to print all rows. + + PUSH AF ; Save the character. + PUSH HL ; Save address of the editing area information. + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + POP HL ; Retrieve address of the editing area information. + POP AF ; Retrieve the character. + + CP #20 ; Was the character a space? + JR NZ,L301A ; Jump back if not to delete the next character until the end of the word is found. + +L302F: CALL L2A1A ; Get character from current cursor position. + CP #20 ; Is it a space? + SCF ; Signal do not produce an error beep. + JR NZ,L2FF8 ; Jump if not to print all rows. + + PUSH HL ; Save address of the editing area information. + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + POP HL ; Retrieve address of the editing area information. + JR L302F ; Jump back to delete all subsequent spaces until the start of the next word or the end of the line is found. + +; ------------------------------------------- +; DELETE-TO-START-OF-LINE Key Handler Routine +; ------------------------------------------- +; Delete to the start of the current BASIC line. Since the function works by deleting one character at +; a time, display file updates are disabled whilst the function is executing to prevent screen flicker. +; An error beep is not produced if there is no characters in the current BASIC line. +; +; Symbol: |<-- DEL +; |<-- +; +; Exit: Carry flag set to indicate not to produce an error beep. + +L303E: CALL L3084 ; Remove cursor attribute, disable display file updates and get current cursor position. Exits with HL pointing to the editing area information. + +L3041: PUSH HL ; Save address of the editing area information. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + BIT 0,(HL) ; Is it the first row of the BASIC line? + JR NZ,L3059 ; Jump if so. + +;Not in the first row of a BASIC line + + CALL L2B5B ; Is previous column position editable? (Returns carry flag set if editable) + JR NC,L306D ; Jump if not editable since nothing to delete. + + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + POP HL ; Retrieve address of the editing area information. + JR L3041 ; Jump back to delete next character until first row of the BASIC line is found. + + PUSH HL ; [Redundant byte] + +;In the first row of the BASIC line + +L3059: LD A,B ; Fetch the new end column number. + CP #00 ; Is it at the start of the row? + JR Z,L306D ; Jump if so since nothing to delete. + + DEC B ; Point to previous column. + CALL L2A1A ; Get character from current cursor position. + INC B ; Point back to the new end column. + CP #00 ; Is it a null character, i.e. not editable? + JR Z,L306D ; Jump if so since nothing to delete. + + DEC B ; Point to previous column. + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + JR L3059 ; Jump back to delete the next character until the start of the BASIC line is found. + +L306D: POP HL ; Retrieve address of the editing area information. + +L306E: SCF ; Signal not to produce error beep. + JP L2FF8 ; Jump back to print all rows. + +; ----------------------------------------- +; DELETE-TO-END-OF-LINE Key Handler Routine +; ----------------------------------------- +; Delete to the end of the current BASIC line. Since the function works by deleting one character at +; a time, display file updates are disabled whilst the function is executing to prevent screen flicker. +; An error beep is not produced if there is no characters in the current BASIC line. +; +; Symbol: -->| DEL +; -->| +; +; Exit: Carry flag set to indicate not to produce an error beep. + +L3072: CALL L3084 ; Remove cursor attribute, disable display file updates and get current cursor position. Exits with HL pointing to the editing area information. + +L3075: CALL L2A1A ; Get character from current cursor position. + CP #00 ; Is it a null character, i.e. at end of BASIC line? + SCF ; Signal not to produce an error beep. + JR Z,L306E ; Jump if end of BASIC line to print all rows. + + PUSH HL ; Save address of the editing area information. + CALL L2F12 ; Delete character to the right, shifting subsequent rows as required. + POP HL ; Retrieve address of the editing area information. + JR L3075 ; Jump back to delete the next character until the end of the BASIC line is found. + +; --------------------------------------------------------- +; Remove Cursor Attribute and Disable Updating Display File +; --------------------------------------------------------- +; This routine is called by the DELETE key handler routines. Aside from removing the cursor from the display, +; it prevents display file updates occurring whilst the delete functions are executing. +; Exit: HL=Address of the editing area information. +; A=Cursor column number preferred. +; B=Cursor column number. +; C=Cursor row number. + +L3084: LD HL,#EC0D ; Editor flags. + RES 0,(HL) ; Signal that the Screen Line Edit Buffer is not full. + + CALL L29EC ; Remove cursor, restoring old attribute. + + LD HL,#EEF5 ; + SET 2,(HL) ; Indicate not to print edit buffer rows, therefore preventing intermediate screen updates. + + LD HL,#F6F1 ; Point to the editing area information. + RET ; + +; ----------------------------------------------------- +; Previous Character Exists in Screen Line Edit Buffer? +; ----------------------------------------------------- +; This routine tests the whether a previous character exists in the current BASIC line within +; the Screen Line Edit Buffer. +; Entry: C=Row number. +; B=Column number. +; Exit : Zero flag set if at start of the BASIC line (first column or leading null). + +L3095: CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD HL,#0020 ; + ADD HL,DE ; HL=Address of the flag byte for this row. + BIT 0,(HL) ; Is this the first row of a BASIC line? + JR Z,L30AE ; Jump if not. + +;On first row of a BASIC line + + LD A,B ; Fetch the column number. + CP #00 ; At the start of the row? + JR Z,L30B2 ; Jump ahead if so. + + DEC B ; Move to the previous column. + CALL L2A1A ; Get current character from Screen Line Edit Buffer. + + INC B ; Move back to the original column. + CP #00 ; Does the position contain a null? + JR Z,L30B2 ; Jump if not. + +L30AE: LD A,#01 ; + OR A ; Reset the zero flag. + RET ; + +L30B2: XOR A ; Set the zero flag. + RET ; + +; ------------------------------------------- +; Find Row Address in Screen Line Edit Buffer +; ------------------------------------------- +; Find address in Screen Line Edit Buffer of specified row. +; This routine calculates DE = #EC16 + #0023*C. +; Entry: C=Row number. +; Exit : DE=Address of edit row. + +L30B4: LD HL,#EC16 ; Point to the Screen Line Edit Buffer. + +L30B7: PUSH AF ; Save A. + + LD A,C ; A=Edit row number. + LD DE,#0023 ; 35 bytes per row. + +L30BC: OR A ; Row requested found? + JR Z,L30C3 ; Jump to exit if so. + + ADD HL,DE ; Advance to next row. + DEC A ; + JR L30BC ; Jump to test if requested row found. + +L30C3: EX DE,HL ; Transfer address to DE. + + POP AF ; Restore A. + RET ; + +; -------------------------------------------- +; Find Position within Screen Line Edit Buffer +; -------------------------------------------- +; Find the address of a specified row and column in the Screen Line Edit Buffer. +; The routine calculates DE = #EC16 + #0023*C + B. +; Entry: B=Column number. +; C=Row number. +; Exit : HL=Address of specified position. + +; [Not used by the ROM] + +L30C6: PUSH DE ; + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the row specified in C. + LD H,#00 ; + LD L,B ; + ADD HL,DE ; DE = #EC16 + #0023*C + B. + POP DE ; + RET ; + +; -------------------------------------- +; Below-Screen Line Edit Buffer Settings +; -------------------------------------- +; This table holds the default values for the Below-Screen Line Edit Buffer settings starting at #F6F5. It should only contain a table of 3 bytes to tie up +; with the space allocated within the Editor workspace variables at #F6F5. As a result, the last 2 bytes will get copied into the Below-Screen Line Edit Buffer +; itself. It appears that the word at #F6F6 is supposed to be a pointer to the next available or accessed location within the buffer but this facility +; is never used. Therefore the table need only be 1 byte long, in which case it would be more efficient for the routine at #30D6 (ROM 0) to simply set the byte +; at #F6F5 directly. + +L30D0: DEFB #05 ; Number of bytes in table. + DEFB #00 ; #F6F5 = Number of rows held in the Below-Screen Line Edit Buffer. + DEFW #0000 ; #F6F6/7. [*BUG* - These two bytes should not be here and the table should only contain 3 bytes. Credit: Paul Farrow] + DEFW #F6F8 ; #F6F8/9 = Points to next location within the Below-Screen Line Edit Buffer. + +; ------------------------------------------ +; Set Below-Screen Line Edit Buffer Settings +; ------------------------------------------ +; Sets the default values for the Below-Screen Line Edit Buffer settings. +; Copy 5 bytes from #30D1-#30D5 (ROM 0) to #F6F5-#F6F9. + +L30D6: LD HL,L30D0 ; Default Below-Screen Line Edit Buffer settings. + LD DE,#F6F5 ; Destination address. + JP L3FBA ; Copy bytes. + +; ---------------------------------------------- +; Shift Up Rows in Below-Screen Line Edit Buffer +; ---------------------------------------------- +; Shifts up all rows in the Below-Screen Line Edit Buffer, or if empty then +; copies a BASIC line from the program area into the Below-Screen Line Edit Buffer. +; Exit: HL=Address of the Below-Screen Line Edit Buffer. + +L30DF: PUSH BC ; Save BC. + PUSH DE ; Save DE. + + LD HL,#F6F5 ; Point to the Below-Screen Line Edit Buffer details. + PUSH HL ; Save it. + LD A,(HL) ; A=Number of rows held in Below-Screen Line Edit Buffer. + OR A ; Are there any rows below screen? + JR NZ,L3101 ; Jump if so. + +;There are no rows in the Below-Screen Line Edit Buffer + + PUSH HL ; Save the address of the Below-Screen Line Edit Buffer details. + CALL L335F ; Copy 'Insert Keyword Representation Into Keyword Construction Buffer' routine into RAM. + + LD HL,(#F9D7) ; HL=Line number of the BASIC line in the program area being edited. + CALL L3352 ; Create line number representation in the Keyword Construction Buffer of the next BASIC line. + JR NC,L30F8 ; Jump if next line does not exist, with HL holding #0000. + + LD (#F9D7),HL ; Store the new line number. + +L30F8: LD B,H ; + LD C,L ; BC=Line number of the next BASIC line, or last BASIC line in the program. + POP HL ; Retrieve the address of the Below-Screen Line Edit Buffer details. + CALL L32D6 ; Copy the BASIC line into the Below-Screen Line Edit Buffer, or empty the first buffer row if the BASIC line does not exist. + DEC A ; Decrement the count of the number of rows held in the Below-Screen Line Edit Buffer, i.e. assume the rows have been shifted. + JR L3116 ; Jump forward. + +;There are rows in the Below-Screen Line Edit Buffer so shift all rows up + +L3101: LD HL,#EC0D ; Editor flags. + RES 0,(HL) ; Signal that the Screen Line Edit Buffer is not full. + + LD HL,#F6F8 ; Below-Screen Line Edit Buffer, the temporary copy of line being edited. + LD D,H ; + LD E,L ; + LD BC,#0023 ; Move all rows in the Below-Screen Line Edit Buffer up by one row. + ADD HL,BC ; + LD BC,#02BC ; 20 rows. + LDIR ; + DEC A ; Decrement the count of the number of rows held in the Below-Screen Line Edit Buffer. + SCF ; [Redundant since never subsequently checked] + +L3116: POP DE ; DE=Points to number of rows held in the Below-Screen Line Edit Buffer. + LD (DE),A ; Update the number of rows held in the Below-Screen Line Edit Buffer + + LD HL,#F6F8 ; HL=Address of first row in the Below-Screen Line Edit Buffer. + + POP DE ; Restore DE. + POP BC ; Restore BC. + RET ; + +; ------------------------------------------------ +; Shift Down Rows in Below-Screen Line Edit Buffer +; ------------------------------------------------ +; Shifts down all rows in the Below-Screen Line Edit Buffer, or the last Screen Line Edit Buffer +; row contains a complete BASIC line then it empties the Below-Screen Line Edit Buffer. +; Entry: DE=Start address in Screen Line Edit Buffer of the last editing row. +; Exit : Carry flag reset to indicate Below-Screen Line Edit Buffer full. +; A =Number of rows held in the Below-Screen Line Edit Buffer. +; HL=Address of first row in the Below-Screen Line Edit Buffer. + +L311E: PUSH BC ; Save BC. + PUSH DE ; DE=Start address in Screen Line Edit Buffer of the last editing row. + + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the edit buffer row. + LD A,(HL) ; Fetch flag byte. + CPL ; Invert bits. + AND #11 ; + JR NZ,L313F ; Jump if not the first row of the BASIC line or no associated line number stored. + +;First row of the BASIC line or an associated line number stored + + PUSH HL ; HL=Points at flag byte of the last Screen Line Edit Buffer row. + PUSH DE ; DE=Address of the last Screen Line Edit Buffer row. + INC HL ; + LD D,(HL) ; + INC HL ; + LD E,(HL) ; DE=Corresponding BASIC line number. + PUSH DE ; Save it. + + CALL L335F ; Copy 'Insert Keyword Representation Into Keyword Construction Buffer' routine to RAM. + + POP HL ; HL=Corresponding line number for last editing row. + CALL L334A ; Find the closest line number. + JR NC,L313D ; Jump if line does not exist. + + LD (#F9D7),HL ; Store as the line number of the BASIC line being edited. + +L313D: POP DE ; DE=Address of the last Screen Line Edit Buffer row. + POP HL ; HL=Points at flag byte of edit buffer row. + +L313F: BIT 0,(HL) ; Is it the first row of the BASIC line? + LD HL,#F6F5 ; Point to the Below-Screen Line Edit Buffer details. + PUSH HL ; Save the address of the Below-Screen Line Edit Buffer details. + JR Z,L314C ; Jump if not the first row of the BASIC line. + +;The first row of the BASIC line, hence after the shift there will not be a row straggling off the bottom of the screen + + LD A,#00 ; Signal no rows held in the Below-Screen Line Edit Buffer. [Could have saved 1 byte by using XOR A] + SCF ; Signal Below-Screen Line Edit Buffer is not full. + JR L3116 ; Store new flag. + +;Not the first row the BASIC line + +L314C: LD A,(HL) ; Fetch the number of rows held in the Below-Screen Line Edit Buffer. + CP #14 ; Has the bottom of the buffer been reached? + JR Z,L3116 ; Jump if so, with the carry flag reset to indicate the buffer is full. + +;The Below-Screen Line Edit Buffer is not full so copy the last Screen Line Edit Buffer row into the top 'visible' Below-Screen Line Edit Buffer row + + LD BC,#0023 ; Length of an edit buffer row. + LD HL,#F6F8 ; Address of the first row in the Below-Screen Line Edit Buffer. + EX DE,HL ; HL=Address of the last row in the Screen Line Edit Buffer, DE=Address of the first row in the Below-Screen Line Edit Buffer. + LDIR ; Copy the last Screen Line Edit Buffer row into the first Below-Screen Line Edit Buffer row, i.e. the 'visible' edit buffer row. + +;Copy all Below-Screen Line Edit Buffer rows down + + LD HL,#F9D6 ; + LD D,H ; + LD E,L ; DE=End of the last row in the Below-Screen Line Edit Buffer. + LD BC,#0023 ; Length of an edit buffer row. + OR A ; + SBC HL,BC ; HL=End of penultimate row in the Below-Screen Line Edit Buffer. + LD BC,#02BC ; Length of the Below-Screen Line Edit Buffer minus one row. + LDDR ; Shift all the rows down by one. + + INC A ; Increment the number of rows held in the Below-Screen Line Edit Buffer. + SCF ; Signal Below-Screen Line Edit Buffer is not full. + JR L3116 ; Jump to store the number of rows held in the Below-Screen Line Edit Buffer. + +; --------------------------------------------------- +; Insert Character into Below-Screen Line Edit Buffer +; --------------------------------------------------- +; Called when a non-action key is pressed and rows of the BASIC line spans into the Below-Screen Line Edit Buffer and therefore +; require shifting. +; Entry: HL=Current row's flag byte. +; A=Character code to insert at the start of the first row of the Below-Screen Line Edit Buffer. + +L316E: PUSH BC ; Save registers. + PUSH DE ; + + PUSH AF ; Save the character to insert. + + LD B,#00 ; Column 0. + LD C,#01 ; Row 1. + PUSH HL ; Save address of the row's flag byte. + CALL L31C3 ; Find row address specified by C in the Below-Screen Line Edit Buffer, into DE. + POP HL ; Retrieve address of the row's flag byte. + + BIT 3,(HL) ; Is this the end row of the BASIC line? + RES 3,(HL) ; Indicate that it is no longer the end row of the BASIC line. + JR NZ,L31A0 ; Jump if it was the end row of the BASIC line. + +;The row in the Below-Screen Line Edit Buffer is not the last row of the BASIC line. + +;Insert the character into the current row. If a spill from this row occurs then insert that character into the start of the +;following row and shift all existing characters right by one. Repeat this process until all rows have been shifted. + +L3180: CALL L2E41 ; Find first editable position on this row from the previous column to the right, returning column number in B. + POP AF ; A=Character to insert. + +L3184: CALL L16AC ; Insert character into the start of the edit buffer row, shifting the row right. Returns carry flag reset. + JR Z,L31BA ; Jump if the byte shifted out of the last column position was #00, hence no more shifting required. + +;The end character of the row has spilled out so it must be inserted as the first editable character of the following row + + PUSH AF ; Stack the character which needs to be inserted into the next row. + + LD B,#00 ; B=First column in the next row. + INC C ; C=Next row. + LD A,C ; + CP #15 ; Has the bottom row of the Below-Screen Line Edit Buffer been reached, i.e. row 21? + JR C,L31A0 ; Jump ahead if not. + +;The bottom row of the Below-Screen Line Edit Buffer has been reached + + DEC HL ; Point to last character of the current row. + LD A,(HL) ; Get the character. + INC HL ; Point back to the flag byte of this row. + CP #00 ; Is the character a null character? [Could have saved 1 byte by using AND A] + JR Z,L31A0 ; Jump ahead if it is. + +;The Below-Screen Line Edit Buffer is completely full + + PUSH HL ; Save address of the flag byte. + LD HL,#EC0D ; Editor flags. + SET 0,(HL) ; Signal that the Screen Line Edit Buffer (including Below-Screen Line Edit Buffer) is full. + POP HL ; HL=Address of the flag byte. + +;Check whether there is another row to shift + +L31A0: BIT 1,(HL) ; Does the row span onto another row? + SET 1,(HL) ; Signal that the row spans onto another row. + RES 3,(HL) ; Signal not the last row of the BASIC line. + CALL L31C3 ; Find the address of the row specified by C in Below-Screen Line Edit Buffer, into DE. + JR NZ,L3180 ; Jump back if spans onto another row to shift it also. + +;All existing rows have now been shifted but a new row needs to be inserted + + PUSH BC ; B=Column number. C=Row number. + PUSH DE ; DE=Start address of the row in the edit buffer. + CALL L35E6 ; Null all column positions in the edit buffer row. + LD (HL),#08 ; Set the flag byte for the row to indicate it is the last row of the BASIC line. + POP DE ; DE=Start address of the row in the edit buffer. + POP BC ; B=Column number. C=Row number. + + CALL L35F4 ; Indent the row by setting the appropriate number of null characters. + + POP AF ; Get character to insert. + JR L3184 ; Jump back to insert it. + +;The shifting of all rows has completed + +L31BA: LD A,C ; Get the row number. + LD (#F6F5),A ; Store as the number of rows held within the Below-Screen Line Edit Buffer. + SET 3,(HL) ; Mark this row as the last row of the BASIC line. + + POP DE ; Restore registers. + POP BC ; + RET ; + +; ------------------------------------------------- +; Find Row Address in Below-Screen Line Edit Buffer +; ------------------------------------------------- +; Find address in the Below-Screen Line Edit Buffer of specified row. +; This routine calculates DE = #F6F8 + #0023*C. +; Entry: C=Row number. +; Exit : Address of edit row in DE. + +L31C3: LD HL,#F6F8 ; Address of the Below-Screen Line Edit Buffer. + JP L30B7 ; Jump to find the row address and return. + +; ------------------------------------------------------------------------- +; Delete a Character from a BASIC Line in the Below-Screen Line Edit Buffer +; ------------------------------------------------------------------------- +; Delete a character at the specified position, shifting subsequent characters left as applicable. +; Exit: A=Character shifted out of the top row of the Below-Screen Line Edit Buffer. + +L31C9: PUSH BC ; Save registers. + PUSH DE ; + + LD HL,#EC0D ; Editor flags. + RES 0,(HL) ; Signal that the Screen Line Edit Buffer (including Below-Screen Line Edit Buffer) is not full. + + LD A,(#F6F5) ; A=Number of rows held in the Below-Screen Line Edit Buffer. + LD C,A ; C=Number of rows held in the Below-Screen Line Edit Buffer. + OR A ; Are there any rows in the Below-Screen Line Edit Buffer? + LD A,#00 ; A null character. + JR Z,L321B ; Jump if there are no rows. [Redundant check since this routine should never be called if there are no rows in this buffer] + +;There is at least one row in the Below-Screen Line Edit Buffer + +L31D9: CALL L31C3 ; Find the address of the last used row within Below-Screen Line Edit Buffer, into DE. + PUSH AF ; Save the character to insert. + + LD B,#00 ; Start searching from column 0. + CALL L2E41 ; Find editable position on this row to the right, returning column number in B. + JR NC,L31F2 ; Jump if no editable position found, i.e. a blank row. + +;The row is not blank + + POP AF ; A=Character to insert. + +;DE=Address within a row of edit buffer. +;A=Character to shift into right of row. +;B=The column to start shifting at. + + CALL L16C1 ; Insert the character into the end of the edit buffer row, shifting all columns left until the cursor position is reached. + + PUSH AF ; A=Character shifted out, zero flag set if the shifted out character was a null (#00). + PUSH BC ; Save the row number. + + LD B,#00 ; Start searching from column 0. + CALL L2E41 ; Is this now a blank row? i.e. Find editable position on this row to the right, returning column number in B. + POP BC ; C=Row number. + JR C,L3216 ; Jump if editable position found. + +;The row is already blank or the result of the shift has caused it to become blank. +;HL points to the last blank character in the row. + +L31F2: INC HL ; Point to the flag byte for the blank row. + LD A,(HL) ; Fetch the flag byte. + PUSH AF ; Save the flag byte for the blank row. + PUSH BC ; Save the row number. + + LD A,C ; Fetch the row number of this blank row. + CP #01 ; Is this the first row in the Below-Screen Line Edit Buffer? + JR NZ,L3204 ; Jump if not. + +;The first row in the Below-Screen Line Edit Buffer is empty and hence the BASIC line now fits completely on screen, i.e. within the Screen Line Edit Buffer + + LD A,(#EC15) ; The number of editing rows on screen. + LD C,A ; C=Bottom row number in the Screen Line Edit Buffer. + CALL L30B4 ; DE=Start address in Screen Line Edit Buffer of the bottom row, as specified in C. + JR L3208 ; Jump ahead to continue. + +;The blank row is not the first row in the Below-Screen Line Edit Buffer, and hence there are further rows above to be shifted + +L3204: DEC C ; Previous row within the Below-Screen Line Edit Buffer. + CALL L31C3 ; Find the address of the row specified by C in Below-Screen Line Edit Buffer, into DE. + +L3208: POP BC ; Retrieve the row number. + POP AF ; A=Flag byte value for the blank row. + + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row above. + RES 1,(HL) ; Signal that the row above does not span onto another row. + OR (HL) ; Or in the flag bits from the blank row, essentially this will retain the 'last row' bit. + LD (HL),A ; Update the flag byte for the row above. + + LD HL,#F6F5 ; Point to the number of rows held in the Below-Screen Line Edit Buffer. + DEC (HL) ; Decrement the row count. + +;Continue with the next row + +L3216: POP AF ; Fetch the character shifted out from the current row, ready for insertion into the row above. + DEC C ; Previous row. + JR NZ,L31D9 ; Jump back if the character shifted out was not null, i.e. more rows above to shift. + +;All rows in the Below-Screen Line Edit Buffer have been shifted + + SCF ; [Redundant since never subsequently checked] + +L321B: POP DE ; Restore registers. + POP BC ; + RET ; + +; -------------------------------------- +; Above-Screen Line Edit Buffer Settings +; -------------------------------------- +; This table holds the default values for the Below-Screen Line Edit Buffer settings starting at #F9DB. +; It appears that the word at #F9DC is supposed to be a pointer to the next available or accessed location within the buffer but this facility +; is never used. Therefore the table need only be 1 byte long, in which case it would be more efficient for the routine at #3222 (ROM 0) to simply +; set the byte at #F9DB directly. + +L321E: DEFB #03 ; Number of bytes in table. + DEFB #00 ; #F9DB = Number of rows held in the Above-Screen Line Edit Buffer. + DEFW #F9DE ; #F9DC/D = Points to next available location within the Above-Screen Line Edit Buffer. + +; ------------------------------------------ +; Set Above-Screen Line Edit Buffer Settings +; ------------------------------------------ +; Sets the default values for the Above-Screen Line Edit Buffer settings. +; Copy 3 bytes from #321F-#3221 (ROM 0) to #F9DB-#F9DD. + +L3222: LD HL,L321E ; Default Above-Screen Line Edit Buffer settings. + LD DE,#F9DB ; Destination address. + JP L3FBA ; Copy bytes. + +; ---------------------------------------------------- +; Shift Rows Down in the Above-Screen Line Edit Buffer +; ---------------------------------------------------- +; If Above-Screen Line Edit Buffer contains row then decrement the count, i.e. less rows off screen. +; If the Above-Screen Line Edit Buffer is empty then load in the new BASIC line at the top of the screen. +; Exit : HL=Address of next row to use within the Above-Screen Line Edit Buffer. +; Carry flag reset if Above-Screen Line Edit Buffer is empty, i.e. no edit buffer rows were shifted. + +L322B: PUSH BC ; Save registers. + PUSH DE ; + + LD HL,#F9DB ; Point to the Above-Screen Line Edit Buffer settings. + PUSH HL ; Save address of the Above-Screen Line Edit Buffer settings. + + LD A,(HL) ; Fetch number of rows of the BASIC line that are off the top of the screen. + OR A ; Are there any rows off the top of the screen? + JR NZ,L3253 ; Jump if there are. + +;There are no rows of the BASIC line off the top of the screen so use the top line that is visible on screen + + PUSH HL ; Save address of the Above-Screen Line Edit Buffer settings. + + CALL L335F ; Copy 'Insert Keyword Representation Into Keyword Construction Buffer' routine to RAM. + + LD HL,(#FC9A) ; HL=New line number at top of screen. + CALL L334A ; Verify the line number exists, or fetch the next line number if not. + JR NC,L3244 ; Jump if the line does not exist. + + LD (#FC9A),HL ; Store the line number found as the one at the top of screen. + +L3244: LD B,H ; + LD C,L ; BC=New line number at top of screen. + + POP HL ; HL=Address of the Above-Screen Line Edit Buffer settings. + INC HL ; + INC HL ; + INC HL ; Point to the first row of the Above-Screen Line Edit Buffer. + JR NC,L325D ; Jump if the line did not exist. + +;The line specified as the one at the top of the screen does exists + +; [*BUG* - HL points to the start of the first row of the Above-Screen Line Edit Buffer but it should point to the settings fields +; 3 bytes earlier since the call to #32D6 (ROM 0) will advance HL by 3 bytes. +; The bug manifests itself when modifying a BASIC line that spans off the top of the screen. It causes corruption +; to the line number, causing a new BASIC line to be inserted rather than updating the line being edited. When editing lines with +; a high line number, the corrupted line number can end up larger 9999 and hence the line is deemed invalid when Enter is pressed +; to insert the line into the BASIC program. The effects of the bug are often masked by the bug at #2DA1 (ROM 0) which performs LD HL,(#F9DB) +; instead of LD HL,#F9DB and thereby fails to detect when the end of the Above-Screen Line Edit Buffer has been reached. The bug can +; be fixed by inserted three DEC HL instructions before the call to #32D6 (ROM 0). Credit: Paul Farrow] + + CALL L32D6 ; Copy the new BASIC line into the Above-Screen Line Edit Buffer. + + DEC A ; Decrement the count of the number of rows held in the Above-Screen Line Edit Buffer. + EX DE,HL ; HL=Start of the next row in the Above-Screen Line Edit Buffer. + JR L325D ; Jump ahead to continue. + +;There are rows of the BASIC line off the top of the screen + +L3253: LD HL,(#F9DC) ; HL=Address of the next location within the Above-Screen Line Edit Buffer to use. + LD BC,#0023 ; + SBC HL,BC ; Point to the previous row location within the Above-Screen Line Edit Buffer. + SCF ; Signal to update the number of rows held in the Above-Screen Line Edit Buffer. + DEC A ; Decrement the count of the number of rows held in the Above-Screen Line Edit Buffer. + +;A=New number of rows held in the Above-Screen Line Edit Buffer. +;HL=Address of a next row to use within the Above-Screen Line Edit Buffer. +;Carry flag reset if no need to update the count of the number of rows in the Above-Screen Line Edit Buffer. + +L325D: EX DE,HL ; DE=Address of next row to use within the Above-Screen Line Edit Buffer. + POP HL ; HL=Address of the Above-Screen Line Edit Buffer settings. + JR NC,L3262 ; Jump if no need to update the count of the number of rows in the Above-Screen Line Edit Buffer. + + LD (HL),A ; Store the number of rows held in the Above-Screen Line Edit Buffer. + +L3262: INC HL ; + LD (HL),E ; + INC HL ; + LD (HL),D ; Store the address of the next row to use within the Above-Screen Line Edit Buffer. + EX DE,HL ; HL=Address of next row to use within the Above-Screen Line Edit Buffer. + + POP DE ; Restore registers. + POP BC ; + RET ; + +; --------------------------------------------------------------- +; Shift Row Up into the Above-Screen Line Edit Buffer if Required +; --------------------------------------------------------------- +; This routine is used to shift up a Screen Line Edit Buffer or a Below-Screen Line Edit Buffer row into the Above-Screen Line Edit Buffer. +; If shifting the top row of the Screen Line Edit Buffer would result in a straggle into the Above-Screen Line Edit Buffer +; then the top row is shifted into the next available location within the Above-Screen Line Edit Buffer. If the shift would +; place the start of a BASIC line on the top row then the Above-Screen Line Edit Buffer is set as empty. +; The routine is also called when relisting the BASIC program. The first BASIC line may straggle above the screen and so it is necessary to +; load the BASIC line into the Above-Screen Line Edit Buffer. This is achieved by using the Below-Screen Line Edit Buffer as a temporary +; line workspace. This routine is called to shift each row into the Above-Screen Line Edit Buffer as appropriate. +; Entry: DE=Start address of the first row in the Screen Line Edit Buffer, or start address of a Below-Screen Line Edit Buffer row. +; Exit : HL=Address of next row to use within the Below-Screen or Screen Line Edit Buffer. +; Carry flag set if the Line Edit Buffer if not full. + +L326A: PUSH BC ; Save registers. + PUSH DE ; + + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for this row within the Below-Screen or Screen Line Edit Buffer. + LD A,(HL) ; Fetch the flag byte. + CPL ; + AND #11 ; + JR NZ,L3282 ; Jump if not the first row of the BASIC line or no associated line number stored. + +;First row of the BASIC line and associated line number stored + + PUSH DE ; DE=Start address of the row. + PUSH HL ; HL=Address of the flag byte for the row in the Line Edit Buffer. + + INC HL ; + LD D,(HL) ; + INC HL ; + LD E,(HL) ; DE=Line number of the corresponding BASIC line. + LD (#FC9A),DE ; Store this as the line number that is at the top of the screen. + + POP HL ; HL=Address of the flag byte for the row in the Below-Screen or Screen Line Edit Buffer. + POP DE ; DE=Start address of the row. + +L3282: BIT 3,(HL) ; Is this the last row of the BASIC line? + LD HL,#F9DB ; Point to the Above-Screen Line Edit Buffer settings. + PUSH HL ; Stack the address of the Above-Screen Line Edit Buffer settings. + JR Z,L32A0 ; Jump if not the last row of the BASIC line. + +;The last row of the BASIC line + + PUSH HL ; Stack the address of the Above-Screen Line Edit Buffer settings. + + CALL L335F ; Copy 'Insert Keyword Representation Into Keyword Construction Buffer' routine to RAM. + + LD HL,(#FC9A) ; Line number at top of screen. + CALL L3352 ; Create line number representation in the Keyword Construction Buffer of the next BASIC line. + LD (#FC9A),HL ; Update the line number at top of screen. + + POP HL ; HL=Address of the Above-Screen Line Edit Buffer settings. + INC HL ; + INC HL ; + INC HL ; Point to the start of the Above-Screen Line Edit Buffer. + + LD A,#00 ; No rows held in the Above-Screen Line Edit Buffer. [Could have saved 1 byte by using XOR A] + SCF ; Signal to update the number of rows count. + JR L325D ; Jump back to store the new Above-Screen Line Edit Buffer settings. + +;Not the last row of the BASIC line + +L32A0: LD A,(HL) ; Fetch the number of rows held in the Above-Screen or Screen Line Edit Buffer. + CP #14 ; Are there 20 rows, i.e. the buffer is full? + JR Z,L32B3 ; Jump if the buffer is full, with the carry flag reset. + +;Shift the top row of the Screen Line Edit Buffer into the Above-Screen Line Edit Buffer + + INC A ; Increment the count of the number of rows in the Above-Screen Line Edit Buffer. + LD HL,(#F9DC) ; Fetch the address of the next row to use within the Above-Screen Line Edit Buffer. + LD BC,#0023 ; The length of one row in the edit buffer, including the 3 data bytes. + EX DE,HL ; DE=Address of next location within the Above-Screen Line Edit Buffer, HL=Address of the row in the Below-Screen or Screen Line Edit Buffer to store. + LDIR ; Copy the row of the BASIC line into the Above-Screen Line Edit Buffer. + + EX DE,HL ; HL=Address of next row to use within the Above-Screen Line Edit Buffer. + SCF ; Signal to update the count of the number of rows. + JR L325D ; Jump back to store the new Above-Screen Line Edit Buffer settings. + +;Above-Screen Line Edit Buffer is full + +L32B3: POP HL ; HL=Address of the Above-Screen Line Edit Buffer settings. + POP DE ; Restore registers. + POP BC ; + RET ; + +; ------------------------------------------------- +; Find Row Address in Above-Screen Line Edit Buffer +; ------------------------------------------------- +; Find the address in the Above-Screen Line Edit Buffer of the specified row. +; This routine calculates DE = #F9DE + #0023*C. +; Entry: C=Row number. +; Exit : DE=Address of edit row. +; +L32B7: LD HL,#F9DE ; Point to the start of the Above-Screen Line Edit Buffer. + JP L30B7 ; Find the row address. + +; ---------------------------------------------- +; BASIC Line Character Action Handler Jump Table +; ---------------------------------------------- + +L32BD: DEFB #08 ; Number of table entries. + DEFB #0D ; Code: Enter. + DEFW L35CC ; Address of the 'Enter' action handler routine. + DEFB #01 ; Code: NULL. + DEFW L35DA ; Null remaining columns of an edit buffer row. + DEFB #12 ; Code: FLASH. + DEFW L335A ; Fetch next de-tokenized character from the BASIC line within the program area. + DEFB #13 ; Code: BRIGHT. + DEFW L335A ; Fetch next de-tokenized character from the BASIC line within the program area. + DEFB #14 ; Code: INVERSE. + DEFW L335A ; Fetch next de-tokenized character from the BASIC line within the program area. + DEFB #15 ; Code: OVER. + DEFW L335A ; Fetch next de-tokenized character from the BASIC line within the program area. + DEFB #10 ; Code: INK. + DEFW L335A ; Fetch next de-tokenized character from the BASIC line within the program area. + DEFB #11 ; Code: PAPER. + DEFW L335A ; Fetch next de-tokenized character from the BASIC line within the program area. + +; ------------------------------------------------------------------------ +; Copy a BASIC Line into the Above-Screen or Below-Screen Line Edit Buffer +; ------------------------------------------------------------------------ +; Copy a BASIC line into the Above-Screen or Below-Screen Line Edit Buffer, handling indentation. +; Entry: HL=Address of the previous row's flag byte in Above-Screen or Below-Screen Line Edit Buffer. +; BC=Line number corresponding to the row being edited. +; Exit : A=Number of rows in the Above-Screen Line Edit Buffer. +; HL=Address of the first row of the BASIC line being edited in the Above-Screen Line Edit Buffer. +; DE=Address of the last row of the BASIC line being edited in the Above-Screen Line Edit Buffer. + +L32D6: LD D,H ; HL=Address of the previous row's flag byte in the Above-Screen/Below-Screen Line Edit Buffer. + LD E,L ; DE=Address of the previous row's flag byte in the Above-Screen/Below-Screen Line Edit Buffer. + INC DE ; + INC DE ; + INC DE ; Advance to the start of the row in the edit buffer. + PUSH DE ; DE=Address of the start of the BASIC line in the Above-Screen/Below-Screen Line Edit Buffer. + + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the row. + LD (HL),#01 ; Signal the first row of the BASIC line. + INC HL ; + LD (HL),B ; + INC HL ; + LD (HL),C ; Store the corresponding BASIC line number. + + LD C,#01 ; Row 1. + LD B,#00 ; Column 0. + +;Enter a loop to process each character from the current BASIC line + +L32EA: PUSH BC ; Save the column and row numbers. + PUSH DE ; Save the Above-Screen/Below-Screen Line Edit Buffer address. + + LD A,(#EC0E) ; Fetch mode. + CP #04 ; Calculator mode? + CALL NZ,L3517 ; If not then fetch the next de-tokenized character from the BASIC line within the program area. + + POP DE ; Retrieve the Above-Screen/Below-Screen Line Edit Buffer address. + POP BC ; Retrieve the column and row numbers. + JR C,L3307 ; Jump if Editor mode and a character was available (if calculator mode then carry flag was reset by test above). + +;Calculator mode, or Editor mode and a character was not available + + LD A,C ; A=Row number. + CP #01 ; Is it row 1? + LD A,#0D ; A='Enter' character. + JR NZ,L3307 ; Jump if not. + +;Row 1 + + LD A,B ; A=Column number. + OR A ; Is it column 0? + LD A,#01 ; A='Null' character, the code used to indicate to null edit positions. + JR Z,L3307 ; Jump if so. + + LD A,#0D ; A='Enter' character. + +L3307: LD HL,L32BD ; The action handler table. + CALL L3FCE ; Call the action handler routine to process the character. + JR C,L332C ; Jump if no more characters are available. + + JR Z,L32EA ; Jump back if an action handler was found so as to process the next character. + +;A character was available but there was no action handler routine to process it + + PUSH AF ; A=Character. + LD A,#1F ; + CP B ; Exceeded column 31? + JR NC,L3326 ; Jump ahead if not. + +;Exceeded last column + + LD A,#12 ; New flag byte value indicating the row spans onto another row and there is an associated line number. + CALL L3331 ; Mark this row as spanning onto the next and clear the following row's flags. + JR C,L3323 ; Jump ahead if not at bottom of the line edit buffer. + +;At the bottom of the edit buffer so process the line as if an 'Enter' character had been encountered + + POP AF ; Discard the stacked item. + LD A,#0D ; A='Enter' character. + JR L3307 ; Jump back to process the 'Enter' code. + +;The edit buffer has room for another character + +L3323: CALL L35F4 ; Indent the row by setting the appropriate number of null characters in the current Above-Screen Line Edit Buffer row. + +L3326: POP AF ; A=Character. + CALL L35C5 ; Store the character in the current row/column in the Above-Screen Line Edit Buffer. + JR L32EA ; Jump back to handle the next character. + +;No more characters are available + +L332C: POP HL ; HL=Address of the BASIC line being edited in the Above-Screen Line Edit Buffer. + LD A,C ; A=Number of rows in the Above-Screen Line Edit Buffer. + RET Z ; [Redundant since carry flag is always set by here, and zero flag never subsequently checked] + + SCF ; [Redundant since never subsequently checked] + RET ; + +; ------------------------------------------ +; Set 'Continuation' Row in Line Edit Buffer +; ------------------------------------------ +; This routine is used when the insertion of a BASIC line needs to span onto a another row. +; It marks the current row as 'not the last row of the BASIC line' and clears the following +; row's flags +; Entry: DE=Address of start of line edit buffer row. +; B=Column number (will be #20). +; C=Row number. +; A=New flag byte value (will be #12). +; Exit : Carry flag reset if bottom of line edit buffer reached. +; HL=Address of the flag byte for the new row. + +L3331: PUSH AF ; Save the new flag byte value. + CALL L35E6 ; HL=Address of flag byte for the row. + POP AF ; Retrieve the new flag byte value. + XOR (HL) ; Toggle to set 'associated line number' and 'row spans onto another row' flags. + LD (HL),A ; Store the new flag byte value. + + LD A,C ; A=Row number. + CP #14 ; At bottom of line edit buffer? + RET NC ; Return if so. + + INC C ; Advance the row number. + LD HL,#0023 ; + ADD HL,DE ; Point to the start of the next row. + EX DE,HL ; + LD HL,#0020 ; + ADD HL,DE ; Point to the flag byte for the next row. + LD (HL),#00 ; Clear the flags to indicate no BASIC line on this row. + SCF ; Signal still on a row within the edit buffer. + RET ; + + +; ============================ +; BASIC Line Handling Routines +; ============================ + +; ----------------------------------------------------- +; Find Address of BASIC Line with Specified Line Number +; ----------------------------------------------------- +; This routine finds the address of the BASIC line in the program area with the specified line number, +; or the next line is the specified one does not exist. +; Entry: HL=Line number. +; Exit : Carry flag set if line exists. +; DE=Points to the command of the BASIC line within the program area. +; HL=Line number (#0000 for no line number). + +L334A: CALL L34B6 ; Find the address of the BASIC line in the program area with the specified line number. + RET C ; Return if the line exists. + + LD HL,#0000 ; No line number. + RET ; + +; --------------------------------------------------------------------- +; Create Next Line Number Representation in Keyword Construction Buffer +; --------------------------------------------------------------------- +; This routine is used to create a string representation of the line number for the next line after the specified line, +; and store it in the Keyword Construction Buffer. +; Entry: HL=Line number. +; A=Print leading space flag (#00=Print leading space). +; Exit : Carry flag set to indicate specified line exists. +; DE=Points to the command field of the BASIC line. +; HL=Line number, or #0000 if line does not exist. + +L3352: CALL L3430 ; Create next line number representation in the Keyword Construction Buffer. + RET C ; Return if line exists. + + LD HL,#0000 ; Line not found. + RET ; + +; -------------------------------------------------------------------------- +; Fetch Next De-tokenized Character from Selected BASIC Line in Program Area +; -------------------------------------------------------------------------- +; Exit: Carry flag reset if a character was available. +; A=Character fetched. + +L335A: CALL L3517 ; Fetch the next de-tokenized character from the BASIC line within the program area. + CCF ; + RET NC ; Return if a character was available. [*BUG* - This should just be a RET. Its effect is harmless since the routine + ; below has previously been called and hence simply overwrites the data already copied to RAM. Credit: Ian Collier (+3), Andrew Owen (128)] + +; -------------------------------------------------------------------------------------- +; Copy 'Insert Keyword Representation into Keyword Construction Buffer' Routine into RAM +; -------------------------------------------------------------------------------------- +; Copies Insert Keyword Representation Into Keyword Construction Buffer routine into physical RAM bank 7, and resets pointers to indicate +; that there is no BASIC line currently being de-tokenized. + +L335F: LD HL,#0000 ; Signal no line number of command. + LD (#FC9F),HL ; Signal no further character to fetch from the BASIC line within the program area. + LD (#FCA1),HL ; Signal no further character to fetch from the Keyword Construction Buffer. + + LD HL,L3374 ; Source for Insert Keyword Representation Into Keyword Construction Buffer routine. + LD DE,#FCAE ; Destination for Insert Keyword Representation Into Keyword Construction Buffer routine. + LD BC,#00BC ; + LDIR ; Copy the routine to RAM bank 7 at address #FCAE. + RET ; + +; ---------------------------------------------------------------------------------- +; Insert Keyword Representation into Keyword Construction Buffer <<< RAM Routine >>> +; ---------------------------------------------------------------------------------- +; This routine copies a keyword string from ROM 1 into the Keyword Construction Buffer, +; terminating it with an 'end of BASIC line' marker (code ' '+#80). Only standard Spectrum +; keywords are handled by this routine (SPECTRUM and PLAY are processed elsewhere). +; The routine is run from RAM bank 7 at #FCAE so that access to both ROMs is available. +; Depending on the value of A (which should be the ASCII code less #A5, +; e.g. 'RND', the first (48K) keyword, has A=0), a different index into the +; token table is taken. This is to allow speedier lookup since there are never more +; than 15 keywords to advance through. +; Entry: A=Keyword character code-#A5 (range #00-#5A). +; DE=Insertion address within Keyword Construction Buffer. +; +; Copied to physical RAM bank 7 at #FCAE-#FCFC by subroutine at #335F (ROM 0). + +L3374: DI ; Disable interrupts whilst paging. + + LD BC,#7FFD ; + LD D,#17 ; Page in ROM 1, SCREEN 0, no locking, RAM bank 7. + OUT (C),D ; + + CP #50 ; Was the token #F5 or above? + JR NC,L33B1 ; + + CP #40 ; Was the token #E5 or above? + JR NC,L33AA ; + + CP #30 ; Was the token #D5 or above? + JR NC,L33A3 ; + + CP #20 ; Was the token #C5 or above? + JR NC,L339C ; + + CP #10 ; Was the token #B5 or above? + JR NC,L3395 ; + +;Used for token range #A5-#B4 (#00 <= A <= #0F) + + LD HL,TOKENS+#0001 ; #0096. Token table entry "RND" in ROM 1. + JR L33B6 ; + +;Used for token range #B5-#C4 (#10 <= A <= #1F) + +L3395: SUB #10 ; + LD HL,TOKENS+#003A ; #00CF. Token table entry "ASN" in ROM 1. + JR L33B6 ; + +;Used for token range #C5-#D4 (#20 <= A <= #2F) + +L339C: SUB #20 ; + LD HL,TOKENS+#006B ; #0100. Token table entry "OR" in ROM 1. + JR L33B6 ; + +;Used for token range #D5-#E4 (#30 <= A <= #3F) + +L33A3: SUB #30 ; + LD HL,TOKENS+#00A9 ; #013E. Token table entry "MERGE" in ROM 1. + JR L33B6 ; + +;Used for token range #E5-#F4 (#40 <= A <= #4F) + +L33AA: SUB #40 ; + LD HL,TOKENS+#00F6 ; #018B. Token table entry "RESTORE" in ROM 1. + JR L33B6 ; + +;Used for token range #F5-#FF (A >= #50) + +L33B1: SUB #50 ; + LD HL,TOKENS+#013F ; #01D4. Token table entry "PRINT" in ROM 1. + +L33B6: LD B,A ; Take a copy of the index value. + OR A ; If A=0 then already have the entry address. + +L33B8: JR Z,L33C3 ; If indexed item found then jump ahead to copy the characters of the token. + +L33BA: LD A,(HL) ; Fetch a character. + INC HL ; Point to next character. + AND #80 ; Has end of token marker been found? + JR Z,L33BA ; Loop back for next character if not. + + DEC B ; Count down the index of the required token. + JR L33B8 ; Jump back to test whether the required token has been reached. + +; ------------------------------------------- +; Copy Keyword Characters <<< RAM Routine >>> +; ------------------------------------------- +; This routine copies a keyword string from ROM 1 into the Keyword Construction Buffer, +; terminating it with an 'end of BASIC line' marker (code ' '+#80). A leading space will +; be inserted if required and a trailing space is always inserted. +; The routine is run from physical RAM bank 7 so that access to both ROMs is available. +; Entry: HL=Address of keyword string in ROM 1. +; DE=Insertion address within Keyword Construction Buffer. +; +; Copied to physical RAM bank 7 at #FCFD-#FD2D by subroutine at #335F (ROM 0). + +L33C3: LD DE,#FCA3 ; DE=Keyword Construction Buffer. + LD (#FCA1),DE ; Store the start address of the constructed keyword. + + LD A,(#FC9E) ; Print a leading space? + OR A ; + LD A,#00 ; + LD (#FC9E),A ; Signal leading space not required. + JR NZ,L33D9 ; Jump if leading space not required. + + LD A,#20 ; Print a leading space. + LD (DE),A ; Insert a leading space. + INC DE ; Advance to next buffer position. + +L33D9: LD A,(HL) ; Fetch a character of the keyword. + LD B,A ; Store it. + INC HL ; Advance to next keyword character. + LD (DE),A ; Store the keyword character in the BASIC line buffer. + INC DE ; Advance to the next buffer position. + AND #80 ; Test if the end of the keyword string. + JR Z,L33D9 ; Jump back if not to repeat for all characters of the keyword. + + LD A,B ; Get keyword character back. + AND #7F ; Mask off bit 7 which indicates the end of string marker. + DEC DE ; Point back at the last character of the keyword copied into the buffer + LD (DE),A ; and store it. + + INC DE ; Advance to the position in the buffer after the last character of the keyword. + LD A,' '+#80 ; #A0. Space + end marker. + LD (DE),A ; Store an 'end of BASIC line so far' marker. + + LD A,#07 ; + LD BC,#7FFD ; + OUT (C),A ; Page in ROM 0, SCREEN 0, no locking, RAM bank 7. + EI ; Re-enable interrupts. + RET ; + +; ------------------------- +; Identify Token from Table +; ------------------------- +; This routine identifies the string within the Keyword Conversion Buffer and returns +; the character code. The last character of the string to identify has bit 7 set. +; Only 48K mode tokens are identified. +; Exit: Carry flag set if token identified. +; A=Character code. +; +; Copied to RAM at #FD2E-#FD69 by routine at #335F (ROM 0). + +L33F4: DI ; Disable interrupts whilst paging. + LD BC,#7FFD ; + LD D,#17 ; Select ROM 1, SCREEN 0, RAM bank 7. + OUT (C),D ; + + LD HL,TOKENS+1 ; #0096. Address of token table in ROM 1. + LD B,#A5 ; Character code of the first token - 'RND'. + +;Entry point here used to match 128K mode tokens and mis-spelled tokens + +L3401: LD DE,#FD74 ; Keyword Conversion Buffer holds the text to match against. + +L3404: LD A,(DE) ; Fetch a character from the buffer. + AND #7F ; Mask off terminator bit. + CP #61 ; Is it lowercase? + LD A,(DE) ; Fetch the character again from the buffer. + JR C,L340E ; Jump if uppercase. + + AND #DF ; Make the character uppercase. + +L340E: CP (HL) ; Does the character match the current item in the token table? + JR NZ,L341A ; Jump if it does not. + + INC HL ; Point to the next character in the buffer. + INC DE ; Point to the next character in the token table. + AND #80 ; Has the terminator been reached? + JR Z,L3404 ; Jump back if not to test the next character in the token. + +;A match was found + + SCF ; Signal a match was found. + JR L3426 ; Jump ahead to continue. + +L341A: INC B ; The next character code to test against. + JR Z,L3425 ; Jump if all character codes tested. + +;The token does not match so skip to the next entry in the token table + +L341D: LD A,(HL) ; Fetch the character from the token table. + AND #80 ; Has the end terminator been found? + INC HL ; Point to the next character. + JR Z,L341D ; Jump back if no terminator found. + + JR L3401 ; Jump back to test against the next token. + +;All character codes tested and no match found + +L3425: OR A ; Clear the carry flag to indicate no match found. + +;The common exit point + +L3426: LD A,B ; Fetch the character code of the matching token (#00 for no match). + + LD D,#07 ; Select ROM 0, SCREEN 0, RAM bank 7. + LD BC,#7FFD ; + OUT (C),D ; + EI ; Re-enable interrupts. + RET ; <<< Last byte copied to RAM >>> + +; --------------------------------------------------------------------- +; Create Next Line Number Representation in Keyword Construction Buffer +; --------------------------------------------------------------------- +; This routine is used to create a string representation of the line number for the next line after the specified line, +; and store it in the Keyword Construction Buffer. +; Entry: HL=Line number. +; A=Print leading space flag (#00=Print leading space). +; Exit : Carry flag set to indicate specified line available. +; DE=Points to the command field of the BASIC line. +; HL=Line number. + +L3430: CALL L34EA ; Clear BASIC line construction pointers (address of next character in the Keyword Construction Buffer and the + ; address of the next character in the BASIC line within the program area being de-tokenized). + OR A ; [*BUG* - Supposed to be XOR A to ensure that a leading space is shown before a command keyword is printed. + ; However, most of the time the A register will enter the routine holding #00 and so the bug is probably harmless. Credit: Paul Farrow] + LD (#FC9E),A ; Print a leading space flag. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + CALL L34F6 ; Find address of the specified BASIC line, into HL. + JR NC,L3491 ; Jump if suitable line number not found, i.e. end of program reached. + + JR NZ,L344D ; Jump if line number did not match, i.e. is higher than the line requested. + +;The line number requested exists + + LD A,B ; BC=Line number. + OR C ; + JR Z,L344D ; Jump if the first program line requested (line number of 0). + +;Fetch the next line + + CALL L34CF ; Move to the start of the next BASIC line. + CALL L34D9 ; Check whether at the end of the BASIC program. + JR NC,L3491 ; Jump if at the end of the BASIC program. + +;Insert line number into the BASIC Line Construction Buffer + +L344D: LD D,(HL) ; HL=Address of the BASIC line. + INC HL ; + LD E,(HL) ; DE=Line number. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + PUSH DE ; Save the line number. + PUSH HL ; Save the address of the BASIC line+1. + PUSH IX ; Save IX. + + LD IX,#FCA3 ; IX=Keyword Construction Buffer, the location where the line number will be created. + LD (#FCA1),IX ; Store the start of the buffer as the next location to store a character in. + + EX DE,HL ; HL=Line number. + LD B,#00 ; Signal no digit printed yet. + LD DE,#FC18 ; -1000. + CALL L3495 ; Insert the thousand digit. + LD DE,#FF9C ; -100. + CALL L3495 ; Insert the hundred digit. + LD DE,#FFF6 ; -10. + CALL L3495 ; Insert the ten digit. + LD DE,#FFFF ; -1. + CALL L3495 ; Insert the units digits. [Note that this is not designed to handle line number 0, which technically is not supported by Sinclair BASIC. + ; The call would need to be preceded by a LD B,#01 instruction to make this function support a line number of 0. Credit: Ian Collier (+3), Andrew Owen (128)] + + DEC IX ; IX points to previous ASCII digit. + LD A,(IX+#00) ; + OR #80 ; + LD (IX+#00),A ; Set bit 7 to mark it as the end of the line number representation. + + POP IX ; Restore registers. + POP HL ; HL=Address of the BASIC line+1. + POP DE ; DE=Line number. + + INC HL ; HL=Points to length field of the BASIC line. + INC HL ; + INC HL ; HL=Points to the command field of the BASIC line. + LD (#FC9F),HL ; Store it as the next character to fetch when parsing the BASIC line to de-tokenize it. + + EX DE,HL ; DE=Points to the command field of the BASIC line, HL=Line number. + SCF ; Signal line exists. + RET ; + +;End of program reached, no line number available + +L3491: CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + RET ; Return with carry flag reset to signal line does not exist. + +; ------------------------------ +; Insert ASCII Line Number Digit +; ------------------------------ +; Insert text representation of a line number digit in a buffer. +; Insert a #00 character for every leading zero. +; Entry: DE=Subtraction amount (-1000, -100, -10, -1). +; HL=Line number. +; IX=Address of the buffer to write the ASCII line number to. +; B=Indicates if digit printed yet (#00=not printed). +; Exit : IX points to next buffer location. +; B=#01 if digit printed. +; HL=Line number remainder. + +L3495: XOR A ; A=Counter. + +L3496: ADD HL,DE ; Keep adding DE + INC A ; and incrementing the counter + JR C,L3496 ; until there is no carry. + + SBC HL,DE ; Adjust for the last addition and. + DEC A ; counter value that caused the overflow. + +;A=Number of multiples of DE in the line number + + ADD A,#30 ; Convert to an ASCII digit. + LD (IX+#00),A ; Store in the buffer. + CP '0' ; #30. Is it a zero? + JR NZ,L34B1 ; Jump ahead if not. + + LD A,B ; Get the 'digit printed' flag. + OR A ; + JR NZ,L34B3 ; Jump ahead if already printed a digit. + + LD A,#00 ; Otherwise this is a leading zero, so + LD (IX+#00),A ; store a zero byte to indicate 'nothing to print'. + JR L34B3 ; and jump ahead to point to the next buffer location. + +L34B1: LD B,#01 ; Indicate 'digit printed'. + +L34B3: INC IX ; Point to the next buffer location. + RET ; + +; ----------------------------------------------------- +; Find Address of BASIC Line with Specified Line Number +; ----------------------------------------------------- +; This routine finds the address of the BASIC line in the program area with the specified line number, +; or the next line is the specified one does not exist. +; Entry: HL=Line number. +; A=#00 to print a leading space. +; Exit : Carry flag set if line exists. +; DE=Points to the command of the BASIC line within the program area. +; HL=Line number. + +L34B6: CALL L34EA ; Clear BASIC line construction pointers (address of next character in the Keyword Construction Buffer and the + ; address of the next character in the BASIC line within the program area being de-tokenized). + OR A ; [*BUG* - Supposed to be XOR A to ensure that a leading space is shown before a command keyword is printed. + ; However, most of the time the A register will enter the routine holding #00 and so the bug is probably harmless. Credit: Paul Farrow] + LD (#FC9E),A ; Store 'print a leading space' flag. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + CALL L34F6 ; Find the address of the BASIC line with this line number, or the next line otherwise. + JR NC,L3491 ; Jump if does not exist. + + EX DE,HL ; HL=Address of BASIC line. + LD A,L ; + OR H ; Address of #0000, i.e. no line exists? + SCF ; Assume line number found. + JP NZ,L344D ; Jump if a line was found. + + CCF ; Reset carry flag to indicate line number does not exist + JR L3491 ; and jump to make a return. + +; ----------------------- +; Move to Next BASIC Line +; ----------------------- +; Entry: HL=Address of current BASIC line. +; Exit : HL=Address of next BASIC line. +; DE=Address of current BASIC line. + +L34CF: PUSH HL ; Save the address of the original line. + + INC HL ; Skip past the line number. + INC HL ; + LD E,(HL) ; Retrieve the line length into DE. + INC HL ; + LD D,(HL) ; + INC HL ; + ADD HL,DE ; Point to the start of the next line. + + POP DE ; DE=Address of original line. + RET ; + +; -------------------------------- +; Check if at End of BASIC Program +; -------------------------------- +; Check whether at the end of the BASIC program. +; Entry: HL=Address of BASIC line. +; Exit : Carry flag reset if end of BASIC program reached. + +L34D9: LD A,(HL) ; + AND #C0 ; + SCF ; Signal not at end of BASIC. + RET Z ; Return if not at end of program. + + CCF ; Signal at end of BASIC. + RET ; + +; -------------------- +; Compare Line Numbers +; -------------------- +; Compare line number at (HL) has line number held in BC. +; Entry: HL=Address of first line number. +; BC=Second line number. +; Exit : Carry flag and zero flag set if the line number matches. +; Zero flag reset if no match, with carry flag set if line number held in BC +; is lower than the line number pointed to by HL. + +L34E0: LD A,B ; Test the first byte. + CP (HL) ; + RET NZ ; Return if not the same. + + LD A,C ; Test the second byte. + INC HL ; + CP (HL) ; + DEC HL ; + RET NZ ; Return if not the same. + + SCF ; Signal line number matches. + RET ; + +; -------------------------------------- +; Clear BASIC Line Construction Pointers +; -------------------------------------- + +L34EA: PUSH HL ; + + LD HL,#0000 ; + LD (#FCA1),HL ; Signal no next character to fetch from the Keyword Construction Buffer. + LD (#FC9F),HL ; Signal no next character to fetch within the BASIC line in the program area. + + POP HL ; + RET ; + +; -------------------------- +; Find Address of BASIC Line +; -------------------------- +; This routine finds the address of the BASIC line within the program area with the specified line number. +; Entry: HL=Line number to find (#0000 for first program line). +; Exit : Carry flag set if requested or next line exists. +; Zero flag reset if no match, with carry flag set if line number is lower than the first program line number. +; HL=Address of the BASIC line number, or #0000 if line does not exist. +; DE=Address of previous BASIC line number, or #0000 if line does not exist. +; BC=Line number. + +L34F6: PUSH HL ; + POP BC ; BC=Line number. [Quicker to have used the instructions LD B,H / LD C,L] + + LD DE,#0000 ; + + LD HL,(#5C53) ; PROG. Address of the start of BASIC program. + CALL L34D9 ; Test for end of BASIC program. + RET NC ; Return if at end of program. + + CALL L34E0 ; Compare line number at (HL) with BC. + RET C ; Return if line number matches or is lower than the first program line number. + + LD A,B ; + OR C ; + SCF ; + RET Z ; Return with carry and zero flags set if first program line was requested (line number 0). + +L350A: CALL L34CF ; Get address of next BASIC line. + CALL L34D9 ; Test for end of BASIC program. + RET NC ; Return if at end of program. + + CALL L34E0 ; Compare line number at (HL) with BC. + JR NC,L350A ; If line number not the same or greater then back to test next line. + + RET ; Exit with carry flag set if line found. + +; ----------------------------------------------------------------- +; Fetch Next De-tokenized Character from BASIC Line in Program Area +; ----------------------------------------------------------------- +; This routine translates a tokenized BASIC line within the program area into the equivalent 'typed' line, i.e. non-tokenized. +; The line number has been previously converted into a string representation and is held within the Keyword Construction Buffer +; at #FCA3. On each call of this routine, the next character of the BASIC line representation is fetched. Initially this is the +; line number characters from the Keyword Construction Buffer, and then the characters from the program line itself. As a token +; character is encountered, it is converted into its string representation and stored in the Keyword Construction Buffer. Then +; each character of this string is fetched in turn. Once all of these characters have been fetched, the next character will be +; from the last position accessed within the BASIC line in the program area. +; Exit: Carry flag set to indicate that a character was available. +; A=Character fetched. + +L3517: LD HL,(#FCA1) ; Fetch the address of the character within the Keyword Construction Buffer. + LD A,L ; + OR H ; Is there an address defined, i.e. characters still within the buffer to fetch? + JR Z,L353C ; Jump ahead if not. + +;There is a character within the Keyword Construction Buffer + + LD A,(HL) ; Fetch a character from the buffer. + INC HL ; Point to the next character. + CP ' '+#80 ; #A0. Was it a trailing space, i.e. the last character? + LD B,A ; Save the character. + LD A,#00 ; Signal 'print a leading space'. + JR NZ,L3529 ; Jump ahead if not. + + LD A,#FF ; Signal 'do not print a leading space'. + +L3529: LD (#FC9E),A ; Store the 'print a leading space' flag value. + + LD A,B ; Get the character back. + BIT 7,A ; Is it the last character in the buffer, i.e. the terminator bit is set? + JR Z,L3534 ; Jump ahead if not. + + LD HL,#0000 ; Signal no more characters within the Keyword Construction Buffer to fetch. + +L3534: LD (#FCA1),HL ; Store the address of the next line number/keyword character within the construction buffer, or #0000 if no more characters. + AND #7F ; Mask off the terminator bit. + JP L358F ; Jump ahead to continue. [Could have saved 1 byte by using JR #358F (ROM 0)] + +;There is no line number/keyword defined within the buffer so fetch the next tokenized character from the BASIC line in the program area + +L353C: LD HL,(#FC9F) ; Fetch the address of the next character within the BASIC line construction workspace. + LD A,L ; + OR H ; Is there a character defined, i.e. end of line not yet reached? + JP Z,L3591 ; Jump ahead if not. [Could have saved 1 byte by using JR #3591 (ROM 0)] + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + +L3547: LD A,(HL) ; Fetch a character from the buffer. + CP #0E ; Is it the hidden number marker indicating a floating-point representation? + JR NZ,L3554 ; Jump ahead if it is not. + + INC HL ; Skip over it the floating-point representation. + INC HL ; + INC HL ; + INC HL ; + INC HL ; + INC HL ; + JR L3547 ; Jump back to fetch the next character. + +L3554: CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + INC HL ; Point to the next character. + LD (#FC9F),HL ; Store the address of the next command within the BASIC line to fetch. + CP #A5 ; 'RND'. Is the current character a standard '48K' keyword? ('RND' = first 48K keyword) + JR C,L3567 ; Jump ahead if not. + + SUB #A5 ; Reduce command code range to #00-#5A. + +; [*BUG* - The routine assumes all tokens require a leading and trailing space. +; However, this is not true for tokens '<=', '>=' and '<>'. Credit: Ian Collier (+3), Paul Farrow (128)] + +; [To fix the bug, the call to #FCAE would need to be replaced with code such as the following. Credit: Paul Farrow. +; +; PUSH AF +; CALL #FCAE ; Construct a string representation of the keyword in the Keyword Construction Buffer. +; POP AF ; DE=Address of last character copied. +; +; CP #22 ; Was it '<=' or above? +; JR C,#3517 (ROM 0) ; Jump back if not to fetch and return the first character of the keyword string. +; +; CP #25 ; Was it '<>' or below? +; JR NC,#3517 (ROM 0) ; Jump back if not to fetch and return the first character of the keyword string. +; +; LD HL,(#FCA1) ; Is there a leading space? +; LD A,(HL) +; CP ' ' +; JR NZ,NOT_LEADING ; Jump if there is not. +; +; INC HL +; LD (#FCA1),HL ; Skip past the leading space. +; +;NOT_LEADING: +; LD A,#FF ; Signal 'do not print a leading space'. +; LD (#FC9E),A +; +; LD A,(DE) ; Is there a trailing space? +; CP ' '+#80 +; JR NZ,NOT_TRAILING ; Jump if there is not. +; +; DEC DE +; EX DE,HL +; SET 7,(HL) ; Set the terminator bit on the preceding character. +; +;NOT_TRAILING: ; ] + + CALL #FCAE ; Construct a string representation of the keyword in the Keyword Construction Buffer. + JP L3517 ; Jump back to fetch and return the first character of the keyword string. [Could have saved 1 byte by using JR #3517 (ROM 0)] + +;It is not a standard 48K keyword + +L3567: CP #A3 ; Is it a '128K' keyword, i.e. 'SPECTRUM' or 'PLAY'? + JR C,L357B ; Jump if not. + +;It is a 128K keyword + + JR NZ,L3572 ; Jump if it is 'PLAY'. + +;Handle 'SPECTRUM' + + LD HL,L3594 ; Keyword string "SPECTRUM". + JR L3575 ; Jump forward. + +L3572: LD HL,L359C ; Keyword string "PLAY". + +L3575: CALL #FCFD ; Copy the keyword string characters into the Keyword Construction Buffer. + JP L3517 ; Jump back to fetch and return the first character of the keyword string. [Could have saved 1 byte by using JR #3517 (ROM 0)] + +;Not a keyword + +L357B: PUSH AF ; Save the character. + LD A,#00 ; + LD (#FC9E),A ; Signal to print a trailing space. + POP AF ; Get the character back. + CP #0D ; Is it an 'Enter' character? + JR NZ,L358F ; Jump if not to exit. + +;The end of the line was found so signal no further characters to fetch + + LD HL,#0000 ; + LD (#FCA1),HL ; Signal no further character to fetch from the Keyword Construction Buffer. + LD (#FC9F),HL ; Signal no further character to fetch from the BASIC line within the program area. + +L358F: SCF ; Set the carry flag to indicate that a character was available. + RET ; + +;There was no character within the buffer + +L3591: SCF ; + CCF ; Reset the carry flag to indicate that a character was not available. + RET ; + + +; ============================= +; Edit Buffer Routines - Part 2 +; ============================= + +; --------------------- +; Keywords String Table +; --------------------- +; The following strings are terminated by having bit 7 set, referenced at #356D (ROM 0) and #3F87 (ROM 0). +; The table consists of the new 128K mode keywords and mis-spelled keywords. + +L3594: DEFM "SPECTRU" + DEFB 'M'+#80 +L359C: DEFM "PLA" + DEFB 'Y'+#80 + DEFM "GOT" + DEFB 'O'+#80 + DEFM "GOSU" + DEFB 'B'+#80 + DEFM "DEFF" + DEFB 'N'+#80 + DEFM "OPEN" + DEFB '#'+#80 + DEFM "CLOSE" + DEFB '#'+#80 + +; -------------------- +; Indentation Settings +; -------------------- +; Copied to #FD6A-#FD6B. + +L35B9: DEFB #02 ; Number of bytes in table. + DEFB #01 ; Flag never subsequently used. Possibly intended to indicate the start of a new BASIC line and hence whether indentation required. + DEFB #05 ; Number of characters to indent by. + +; ------------------------ +; Set Indentation Settings +; ------------------------ + +L35BC: LD HL,L35B9 ; HL=Address of the indentation settings data table. + LD DE,#FD6A ; Destination address. + JP L3FBA ; Copy two bytes from #35B9-#35BA (ROM 0) to #FD6A-#FD6B. + +; -------------------------------------------- +; Store Character in Column of Edit Buffer Row +; -------------------------------------------- +; Store character in the specified column of the current edit buffer row. +; Entry: B=Column number. +; DE=Start address of row. +; A=Character to insert. +; Exit : B=Next column number. + +L35C5: LD L,B ; + LD H,#00 ; + ADD HL,DE ; Point to the required column. + LD (HL),A ; Store the character. + INC B ; Advance to the next column. + RET ; + +; ------------------------------ +; 'Enter' Action Handler Routine +; ------------------------------ +; Entry: B=Initial column to null. +; DE=Address of start of edit row. +; Exit : Carry flag set to indicate no more characters are available, i.e. end of line. + +L35CC: CALL L35E6 ; Null remaining column positions in the edit buffer row. + + LD A,(HL) ; Fetch the flag byte. + OR #18 ; Signal associated line number and last row in the BASIC line. + LD (HL),A ; Update the flag byte. + + LD HL,#FD6A ; [Redundant since flag never subsequently tested. Deleting these instructions would have saved 5 bytes] + SET 0,(HL) ; Flag possibly intended to indicate the start of a new BASIC line and hence whether indentation required. + + SCF ; Signal no more characters are available, i.e. end of line. + RET ; + +; ------------------------------------- +; 'Null Columns' Action Handler Routine +; ------------------------------------- +; Entry: B=Initial column to null. +; DE=Address of start of edit row. +; Exit : Carry flag set to indicate no more characters are available, i.e. end of line. + +L35DA: CALL L35E6 ; Null remaining column positions in the edit buffer row. + + SET 3,(HL) ; Signal last row of the BASIC line in the row flag byte. + + LD HL,#FD6A ; [Redundant since flag never subsequently tested. Deleting these instructions would have saved 5 bytes] + SET 0,(HL) ; Flag possibly intended to indicate the start of a new BASIC line and hence whether indentation required. + + SCF ; Signal no more characters are available, i.e. end of line. + RET ; + +; --------------------- +; Null Column Positions +; --------------------- +; This routine inserts null characters into the remainder of a line edit buffer row. +; Entry: B=Initial column to null. +; DE=Address of start of edit row. +; Exit : HL=Address of the row's flag byte. + +L35E6: LD L,B ; + LD H,#00 ; HL=Number of columns. + ADD HL,DE ; Point to column position in line edit buffer row. + LD A,#20 ; 32 columns. + +L35EC: CP B ; Found specified column? + RET Z ; Return if so. + + LD (HL),#00 ; Store a null in the location. + INC HL ; Next buffer position. + INC B ; Increment column position counter. + JR L35EC ; Repeat for next column. + +; ---------------------- +; Indent Edit Buffer Row +; ---------------------- +; Indent a row by setting the appropriate number of characters in +; an edit buffer row to nulls, i.e. character #00. +; Entry: DE=Address of row within edit buffer. +; Exit : B=First usable column number in the row. + +L35F4: LD A,(#FD6B) ; Get the number of indentation columns. + LD B,#00 ; Start at first column. + +L35F9: LD H,#00 ; + LD L,B ; HL=Column position. + ADD HL,DE ; + LD (HL),#00 ; Put a null in the column position. + INC B ; Next position. + DEC A ; + JR NZ,L35F9 ; Repeat for all remaining columns. + + RET ; + +; ------------------------------------------------- +; Print Edit Buffer Row to Display File if Required +; ------------------------------------------------- +; Print a row of the edit buffer to the display file if required. +; Entry: HL=Address of edit buffer row. + +L3604: PUSH BC ; Save registers. + PUSH DE ; + PUSH HL ; + + PUSH HL ; Save edit buffer row address. + LD HL,#EEF5 ; + BIT 2,(HL) ; Is printing of the edit buffer row required? + POP HL ; Retrieve edit buffer row address. + JR NZ,L3614 ; Jump if printing is not required. + + LD B,C ; B=Cursor row position. + CALL L3B1E ; Print the edit buffer row to the screen. Returns with the carry flag set. + +L3614: POP HL ; Restore registers. + POP DE ; + POP BC ; + RET ; + +; ---------------------------------------------- +; Shift Up Edit Rows in Display File if Required +; ---------------------------------------------- +; This routine shifts edit rows in the display file up if required, replacing the bottom row with the +; top entry from the Below-Screen Line Edit Buffer. +; Entry: HL=Address of first row within the Below-Screen Line Edit Buffer. +; C =Number of editing rows on screen. +; B =Row number to shift from. + +L3618: PUSH BC ; Save registers. + PUSH DE ; + PUSH HL ; + + PUSH HL ; Save edit buffer row address. + LD HL,#EEF5 ; + BIT 2,(HL) ; Is updating of the display file required? + POP HL ; Retrieve edit buffer row address. + JR NZ,L3628 ; Jump if updating is not required. + + LD E,C ; E=Cursor row position, i.e. row to shift from. + CALL L3ABF ; Shift up edit rows in the display file, replacing the bottom row with the + ; top entry from the Below-Screen Line Edit Buffer. + +L3628: POP HL ; Restore registers. + POP DE ; + POP BC ; + RET ; + +; ------------------------------------------------ +; Shift Down Edit Rows in Display File if Required +; ------------------------------------------------ +; This routine shifts edit rows in the display file down if required, replacing the top row with the +; bottom entry from the Above-Screen Line Edit Buffer. +; Entry: HL=Address of next row to use within the Above-Screen Line Edit Buffer. +; C =Number of editing rows on screen. +; B =Row number to shift from. + +L362C: PUSH BC ; Save registers. + PUSH DE ; + PUSH HL ; + + PUSH HL ; Save edit buffer row address. + LD HL,#EEF5 ; + BIT 2,(HL) ; Is updating of the display file required? + POP HL ; Retrieve edit buffer row address. + JR NZ,L363C ; Jump if updating is not required. + + LD E,C ; E=Cursor row position, i.e. row to shift from. + CALL L3AC6 ; Shift down edit rows in the display file, replacing the top row with the + ; bottom entry from the Above-Screen Line Edit Buffer. + +L363C: POP HL ; Restore registers. + POP DE ; + POP BC ; + RET ; + +; --------------------------- +; Set Cursor Attribute Colour +; --------------------------- +; Entry: C=Row number, B=Column number. + +L3640: PUSH AF ; Save registers. + PUSH BC ; + PUSH DE ; + PUSH HL ; + + LD A,B ; Swap B with C. + LD B,C ; + LD C,A ; + CALL L3A9D ; Set cursor position attribute. + + POP HL ; Restore registers. + POP DE ; + POP BC ; + POP AF ; + RET ; + +; ------------------------------------------ +; Restore Cursor Position Previous Attribute +; ------------------------------------------ +; Entry: C=row, B=column. + +L364F: PUSH AF ; Save registers + PUSH BC ; + PUSH DE ; + PUSH HL ; + + LD A,B ; Column. + LD B,C ; Row. + LD C,A ; Column. + CALL L3AB2 ; Restore cursor position attribute. + + POP HL ; Restore registers. + POP DE ; + POP BC ; + POP AF ; + RET ; + +; -------------- +; Reset 'L' Mode +; -------------- + +L365E: LD A,#00 ; Select 'L' mode. + LD (#5C41),A ; MODE. + + LD A,#02 ; Reset repeat key duration. + LD (#5C0A),A ; REPPER + +L3668: LD HL,#5C3B ; FLAGS. + LD A,(HL) ; + OR #0C ; Select L-Mode and Print in L-Mode. + LD (HL),A ; + + LD HL,#EC0D ; Editor flags. + BIT 4,(HL) ; Return to the calculator? + LD HL,FLAGS3 ; #5B66. + JR NZ,L367C ; Jump ahead if so. + + RES 0,(HL) ; Select Editor/Menu mode. + RET ; + +L367C: SET 0,(HL) ; Select BASIC/Calculator mode. + RET ; + +; -------------------- +; Wait for a Key Press +; -------------------- +; Exit: A holds key code. + +L367F: PUSH HL ; Preserve contents of HL. + +L3680: LD HL,#5C3B ; FLAGS. + +L3683: BIT 5,(HL) ; + JR Z,L3683 ; Wait for a key press. + + RES 5,(HL) ; Clear the new key indicator flag. + + LD A,(#5C08) ; Fetch the key pressed from LAST_K. + LD HL,#5C41 ; MODE. + RES 0,(HL) ; Remove extended mode. + + CP #20 ; Is it a control code? + JR NC,L36A2 ; Jump if not to accept all characters and token codes (used for the keypad). + + CP #10 ; Is it a cursor key? + JR NC,L3680 ; Jump back if not to wait for another key. + + CP #06 ; Is it a cursor key? + JR C,L3680 ; Jump back if not to wait for another key. + +;Control code or cursor key + + CALL L36A4 ; Handle CAPS LOCK code and 'mode' codes. + JR NC,L3680 ; Jump back if mode might have changed. + +L36A2: POP HL ; Restore contents of HL. + RET ; + +L36A4: RST #28 ; + DEFW KEY_M_CL ; #10DB. Handle CAPS LOCK code and 'mode' codes via ROM 1. + RET ; + + +; ====================== +; MENU ROUTINES - PART 5 +; ====================== + +; ------------ +; Display Menu +; ------------ +; HL=Address of menu text. + +L36A8: PUSH HL ; Save address of menu text. + + CALL L373B ; Store copy of menu screen area and system variables. + + LD HL,#5C3C ; TVFLAG. + RES 0,(HL) ; Signal using main screen. + + POP HL ; HL=Address of menu text. + + LD E,(HL) ; Fetch number of table entries. + INC HL ; Point to first entry. + + PUSH HL ; + LD HL,L37EC ; Set title colours. + CALL L3733 ; Print them. + POP HL ; + + CALL L3733 ; Print menu title pointed to by HL. + + PUSH HL ; + CALL L3822 ; Print Sinclair stripes. + LD HL,L37FA ; Black ' '. + CALL L3733 ; Print it. + POP HL ; HL=Address of first menu item text. + + PUSH DE ; Save number of menu items left to print. + + LD BC,#0807 ; + CALL L372B ; Perform 'Print AT 8,7;' (this is the top left position of the menu). + +L36D1: PUSH BC ; Save row print coordinates. + + LD B,#0C ; Number of columns in a row of the menu. + + LD A,#20 ; Print ' '. + RST #10 ; + +L36D7: LD A,(HL) ; Fetch menu item character. + INC HL ; + CP #80 ; End marker found? + JR NC,L36E0 ; Jump if end of text found. + + RST #10 ; Print menu item character + DJNZ L36D7 ; Repeat for all characters in menu item text. + +L36E0: AND #7F ; Clear bit 7 to yield a final text character. + RST #10 ; Print it. + +L36E3: LD A,#20 ; + RST #10 ; Print trailing spaces + DJNZ L36E3 ; Until all columns filled. + + POP BC ; Fetch row print coordinates. + INC B ; Next row. + CALL L372B ; Print AT. + + DEC E ; + JR NZ,L36D1 ; Repeat for all menu items. + + LD HL,#6F38 ; Coordinates, pixel (111, 56) = end row 13, column 7. + + POP DE ; Fetch number of menu items to E. + SLA E ; + SLA E ; + SLA E ; Determine number of pixels to span all menu items. + LD D,E ; + DEC D ; D=8*Number of menu items - 1. + + LD E,#6F ; Number of pixels in width of menu. + LD BC,#FF00 ; B=-1, C=0. Plot a vertical line going up. + LD A,D ; A=Number of vertical pixels to plot. + CALL L3719 ; Plot line. + + LD BC,#0001 ; B=0, C=1. Plot a horizontal line going to the right. + LD A,E ; A=Number of horizontal pixels to plot. + CALL L3719 ; Plot line. + + LD BC,#0100 ; B=1, C=0. Plot a vertical line going down. + LD A,D ; A=Number of vertical pixels to plot. + INC A ; Include end pixel. + CALL L3719 ; Plot line. + + XOR A ; A=Index of menu option to highlight. + CALL L37CA ; Toggle menu option selection so that it is highlight. + RET ; [Could have saved one byte by using JP #37CA (ROM 0)] + +; ----------- +; Plot a Line +; ----------- +; Entry: H=Line pixel coordinate. +; L=Column pixel coordinate. +; B=Offset to line pixel coordinate (#FF, #00 or #01). +; C=Offset to column pixel coordinate (#FF, #00 or #01). +; A=number of pixels to plot. + +L3719: PUSH AF ; Save registers. + PUSH HL ; + PUSH DE ; + PUSH BC ; + + LD B,H ; Coordinates to BC. + LD C,L ; + RST #28 ; + DEFW PLOT_SUB+4 ; #22E9. Plot pixel + + POP BC ; Restore registers. + POP DE ; + POP HL ; + POP AF ; + + ADD HL,BC ; Determine coordinates of next pixel. + DEC A ; + JR NZ,L3719 ; Repeat for all pixels. + + RET ; + +; ------------------------- +; Print "AT B,C" Characters +; ------------------------- + +L372B: LD A,#16 ; 'AT'. + RST #10 ; Print. + LD A,B ; B=Row number. + RST #10 ; Print. + LD A,C ; C=Column number. + RST #10 ; Print. + RET ; + +; ------------ +; Print String +; ------------ +; Print characters pointed to by HL until #FF found. + +L3733: LD A,(HL) ; Fetch a character. + INC HL ; Advance to next character. + CP #FF ; Reach end of string? + RET Z ; Return if so. + + RST #10 ; Print the character. + JR L3733 ; Back for the next character. + +; ---------------------- +; Store Menu Screen Area +; ---------------------- +; Store copy of menu screen area and system variables. + +L373B: SCF ; Set carry flag to signal to save screen area. + JR L373F ; Jump ahead to continue. + +; ------------------------ +; Restore Menu Screen Area +; ------------------------ +; Restore menu screen area and system variables from copy. +; Entry: IX=Address of the cursor settings information. + +L373E: AND A ; Reset carry flag to signal restore screen area. + +L373F: LD DE,#EEF6 ; Store for TVFLAG. + LD HL,#5C3C ; TVFLAG. + JR C,L3748 ; Jump if storing copies. + + EX DE,HL ; Exchange source and destination pointers. + +L3748: LDI ; Transfer the byte. + JR C,L374D ; Jump if storing copies. + + EX DE,HL ; Restore source and destination pointers. + +L374D: LD HL,#5C7D ; COORDS. DE=#EEF7 by now. + JR C,L3753 ; Jump if storing copies. + + EX DE,HL ; Exchange source and destination pointers. + +L3753: LD BC,#0014 ; Copy 20 bytes. + LDIR ; Copy COORDS until ATTR_T. + JR C,L375B ; Jump if storing copies. + + EX DE,HL ; Restore source and destination pointers. + +L375B: EX AF,AF' ; Save copy direction flag. + + LD BC,#0707 ; Menu will be at row 7, column 7. + CALL L3B94 ; B=Number of rows to end row of screen. C=Number of columns to the end column of the screen. + + LD A,(IX+#01) ; A=Rows above the editing area (#16 when using the lower screen, #00 when using the main screen). + ADD A,B ; B=Row number within editing area. + LD B,A ; B=Bottom screen row to store. + LD A,#0C ; A=Number of rows to store. [Could have been just #07 freeing up 630 bytes of workspace] + +L3769: PUSH BC ; B holds number of row to store. + PUSH AF ; A holds number of rows left to store. + PUSH DE ; DE=End of destination address. + + RST #28 ; + DEFW CL_ADDR ; #0E9B. HL=Display file address of row B. + LD BC,#0007 ; Menu always starts at column 7. + ADD HL,BC ; HL=Address of attribute byte at column 7. + POP DE ; + + CALL L377E ; Store / restore menu screen row. + + POP AF ; + POP BC ; + DEC B ; Next row. + DEC A ; More rows to store / restore? + JR NZ,L3769 ; Repeat for next row + + RET ; + +; ------------------------------- +; Store / Restore Menu Screen Row +; ------------------------------- +; Entry: HL=Start address of menu row in display file. +; DE=Screen location/Workspace store for screen row. +; AF'=Carry flag set for store to workspace, reset for restore to screen. +; Exit : DE=Screen location/workspace store for next screen row. + +;Save the display file bytes + +L377E: LD BC,#080E ; B=Menu row is 8 lines deep. C=Menu is 14 columns wide. + +L3781: PUSH BC ; Save number of row lines. + LD B,#00 ; Just keep the column count in BC. + + PUSH HL ; Save display file starting address. + + EX AF,AF' ; Retrieve copy direction flag. + JR C,L3789 ; Jump if storing copies of display file bytes. + + EX DE,HL ; Exchange source and destination pointers. + +L3789: LDIR ; Copy the row of menu display file bytes. + JR C,L378E ; Jump if storing copies of display file bytes. + + EX DE,HL ; Restore source and destination pointers. + +L378E: EX AF,AF' ; Save copy direction flag. + + POP HL ; Fetch display file starting address. + INC H ; Advance to next line + + POP BC ; Fetch number of lines. + DJNZ L3781 ; Repeat for next line. + +;Now save the attributes + + PUSH BC ; B=0. C=Number of columns. + PUSH DE ; DE=Destination address. + RST #28 ; + DEFW CL_ATTR ; #0E88. HL=Address of attribute byte. + EX DE,HL ; DE=Address of attribute byte. + POP DE ; + POP BC ; + + EX AF,AF' ; Retrieve copy direction flag. + JR C,L37A0 ; Jump if storing copies of attribute bytes. + + EX DE,HL ; Restore source and destination pointers. + +L37A0: LDIR ; Copy the row of menu attribute bytes. + JR C,L37A5 ; Jump if storing copies of attribute bytes. + + EX DE,HL ; Restore source and destination pointers. + +L37A5: EX AF,AF' ; Save copy direction flag. + RET ; + +; ------------ +; Move Up Menu +; ------------ + +L37A7: CALL L37CA ; Toggle old menu item selection to de-highlight it. + DEC A ; Decrement menu index. + JP P,L37B1 ; Jump if not exceeded top of menu. + + LD A,(HL) ; Fetch number of menu items. + DEC A ; Ignore the title. + DEC A ; Make it indexed from 0. + +L37B1: CALL L37CA ; Toggle new menu item selection to highlight it. + SCF ; Ensure carry flag is set to prevent immediately + RET ; calling menu down routine upon return. + +; -------------- +; Move Down Menu +; -------------- + +L37B6: PUSH DE ; Save DE. + + CALL L37CA ; Toggle old menu item selection to de-highlight it. + + INC A ; Increment menu index. + LD D,A ; Save menu index. + + LD A,(HL) ; fetch number of menu items. + DEC A ; Ignore the title. + DEC A ; Make it indexed from 0. + CP D ; Has bottom of menu been exceeded? + LD A,D ; Fetch menu index. + JP P,L37C5 ; Jump if bottom menu not exceeded. + + XOR A ; Select top menu item. + +L37C5: CALL L37CA ; Toggle new menu item selection to highlight it. + + POP DE ; Restore DE. + RET ; + +; -------------------------------------- +; Toggle Menu Option Selection Highlight +; -------------------------------------- +; Entry: A=Menu option index to highlight. + +L37CA: PUSH AF ; Save registers. + PUSH HL ; + PUSH DE ; + + LD HL,#5907 ; First attribute byte at position (9,7). + LD DE,#0020 ; The increment for each row. + AND A ; + JR Z,L37DA ; Jump ahead if highlighting the first entry. + +L37D6: ADD HL,DE ; Otherwise increase HL + DEC A ; for each row. + JR NZ,L37D6 ; + +L37DA: LD A,#78 ; Flash 0, Bright 1, Paper 7, Ink 0 = Bright white. + CP (HL) ; Is the entry already highlighted? + JR NZ,L37E1 ; Jump ahead if not. + + LD A,#68 ; Flash 0, Bright 1, Paper 5, Ink 0 = Bright cyan. + +L37E1: LD D,#0E ; There are 14 columns to set. + +L37E3: LD (HL),A ; Set the attributes for all columns. + INC HL ; + DEC D ; + JR NZ,L37E3 ; + + POP DE ; Restore registers. + POP HL ; + POP AF ; + RET ; + +; ------------------------ +; Menu Title Colours Table +; ------------------------ + +L37EC: DEFB #16, #07, #07 ; AT 7,7; + DEFB #15, #00 ; OVER 0; + DEFB #14, #00 ; INVERSE 0; + DEFB #10, #07 ; INK 7; + DEFB #11, 00 ; PAPER 0; + DEFB #13, #01 ; BRIGHT 1; + DEFB #FF ; + +; ---------------------- +; Menu Title Space Table +; ---------------------- + +L37FA: DEFB #11, #00 ; PAPER 0; + DEFB ' ' ; + DEFB #11, #07 ; PAPER 7; + DEFB #10, #00 ; INK 0; + DEFB #FF ; + +; ----------------------------- +; Menu Sinclair Stripes Bitmaps +; ----------------------------- +; Bit-patterns for the Sinclair stripes used on the menus. + +L3802: DEFB #01 ; 0 0 0 0 0 0 0 1 X + DEFB #03 ; 0 0 0 0 0 0 1 1 XX + DEFB #07 ; 0 0 0 0 0 1 1 1 XXX + DEFB #0F ; 0 0 0 0 1 1 1 1 XXXX + DEFB #1F ; 0 0 0 1 1 1 1 1 XXXXX + DEFB #3F ; 0 0 1 1 1 1 1 1 XXXXXX + DEFB #7F ; 0 1 1 1 1 1 1 1 XXXXXXX + DEFB #FF ; 1 1 1 1 1 1 1 1 XXXXXXXX + + DEFB #FE ; 1 1 1 1 1 1 1 0 XXXXXXX + DEFB #FC ; 1 1 1 1 1 1 0 0 XXXXXX + DEFB #F8 ; 1 1 1 1 1 0 0 0 XXXXX + DEFB #F0 ; 1 1 1 1 0 0 0 0 XXXX + DEFB #E0 ; 1 1 1 0 0 0 0 0 XXX + DEFB #C0 ; 1 1 0 0 0 0 0 0 XX + DEFB #80 ; 1 0 0 0 0 0 0 0 X + DEFB #00 ; 0 0 0 0 0 0 0 0 + +; --------------------- +; Sinclair Strip 'Text' +; --------------------- +; CHARS points to RAM at #5A98, and characters ' ' and '!' redefined +; as the Sinclair strips using the bit patterns above. + +L3812: DEFB #10, #02, ' ' ; INK 2; + DEFB #11, #06, '!' ; PAPER 6; + DEFB #10, #04, ' ' ; INK 4; + DEFB #11, #05, '!' ; PAPER 5; + DEFB #10, #00, ' ' ; INK 0; + DEFB #FF ; + +; -------------------------------------- +; Print the Sinclair stripes on the menu +; -------------------------------------- + +L3822: PUSH BC ; Save registers. + PUSH DE ; + PUSH HL ; + + LD HL,L3802 ; Graphics bit-patterns + LD DE,STRIP1 ; #5B98. + LD BC,#0010 ; Copy two characters. + LDIR ; + + LD HL,(#5C36) ; Save CHARS. + PUSH HL ; + + LD HL,STRIP1-#0100 ; #5A98. + LD (#5C36),HL ; Set CHARS to point to new graphics. + + LD HL,L3812 ; Point to the strip string. + CALL L3733 ; Print it. + + POP HL ; Restore CHARS. + LD (#5C36),HL ; + + POP HL ; Restore registers. + POP DE ; + POP BC ; + RET ; + +; ------------------------ +; Print '128 BASIC' Banner +; ------------------------ +;[v]..................................................................................................................... SP2000 [v] +;L3848: LD HL,L2769 ; "128 BASIC" text from main menu. +L3848: LD HL,B128 ; "128 BASIC" text from main menu. +;[^]..................................................................................................................... SP2000 [^] + JR L385A ; Jump ahead to print banner. + +; ------------------------- +; Print 'Calculator' Banner +; ------------------------- +;[v]..................................................................................................................... SP2000 [v] +;L384D: LD HL,L2772 ; "Calculator" text from main menu. +L384D: LD HL,BCALC ; "Calculator" text from main menu. +;[^]..................................................................................................................... SP2000 [^] + JR L385A ; Jump ahead to print banner. + +; -------------------------- +; Print 'Tape Loader' Banner +; -------------------------- +;[v]..................................................................................................................... SP2000 [v] +;L3852: LD HL,L275E ; "Tape Loader" text from main menu. +L3852: LD HL,BISD ; "Hardware" text from main menu. +;[^]..................................................................................................................... SP2000 [^] + JR L385A ; Jump ahead to print banner. + +; -------------------------- +; Print 'Tape Tester' Banner +; -------------------------- +;[v]..................................................................................................................... SP2000 [v] +;L3857: LD HL,L2784 ; "Tape Tester" text from main menu. +L3857: LD HL,BSERV ; "Options" text from main menu. +; JR L385A ; Jump ahead to print banner. +;[^]..................................................................................................................... SP2000 [^] + +; ------------ +; Print Banner +; ------------ +L385A: PUSH HL ; Address in memory of the text of the selected menu item. + + CALL L3881 ; Clear lower editing area display. + + LD HL,#5AA0 ; Address of banner row in attributes. + LD B,#20 ; 32 columns. + LD A,#40 ; FLASH 0, BRIGHT 1, PAPER 0, INK 0. + +L3865: LD (HL),A ; Set a black row. + INC HL ; + DJNZ L3865 ; + + LD HL,L37EC ; Menu title colours table. + CALL L3733 ; Print the colours as a string. + + LD BC,#1500 ; + CALL L372B ; Perform 'Print AT 21,0;'. + + POP DE ; Address in memory of the text of the selected menu item. + CALL L057D ; Print the text. + + LD C,#1A ; B has not changed and still holds 21. + CALL L372B ; Perform 'Print AT 21,26;'. + JP L3822 ; Print Sinclair stripes and return to calling routine. + +; --------------------------- +; Clear Lower Editing Display +; --------------------------- +CLS_X: +L3881: LD B,#15 ; Top row of editing area. + LD D,#17 ; Bottom row of editing area. + JP L3B5E ; Reset Display. + + +; ================ +; RENUMBER ROUTINE +; ================ +; Exit: Carry flag reset if required to produce an error beep. + +L3888: CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + CALL L3A05 ; DE=Count of the number of BASIC lines. + LD A,D ; + OR E ; Were there any BASIC lines? + JP Z,L39C0 ; Jump if not to return since there is nothing to renumber. + + LD HL,(RNSTEP) ; #5B96. Fetch the line number increment for Renumber. + RST #28 ; + DEFW HL_MULT_DE ; #30A9. HL=HL*DE in ROM 1. HL=Number of lines * Line increment = New last line number. + ; [*BUG* - If there are more than 6553 lines then an arithmetic overflow will occur and hence + ; the test below to check if line 9999 would be exceeded will fail. The carry flag will be set + ; upon such an overflow and simply needs to be tested. The bug can be resolved by following the + ; call to HL_MULT_DE with a JP C,#39C0 (ROM 0) instruction. Credit: Ian Collier (+3), Andrew Owen (128)] + EX DE,HL ; DE=Offset of new last line number from the first line number. + + LD HL,(RNFIRST) ; #5B94. Starting line number for Renumber. + ADD HL,DE ; HL=New last line number. + LD DE,#2710 ; 10000. + OR A ; + SBC HL,DE ; Would the last line number above 9999? + JP NC,L39C0 ; Jump if so to return since Renumber cannot proceed. + +;There is a program that can be renumbered + + LD HL,(#5C53) ; PROG. HL=Address of first BASIC line. + +L38AA: RST #28 ; Find the address of the next BASIC line from the + DEFW NEXT_ONE ; #19B8. location pointed to by HL, returning it in DE. + + INC HL ; Advance past the line number bytes to point + INC HL ; at the line length bytes. + LD (RNLINE),HL ; #5B92. Store the address of the BASIC line's length bytes. + + INC HL ; Advance past the line length bytes to point + INC HL ; at the command. + LD (N_STR1+4),DE ; #5B6B. Store the address of the next BASIC line. + +L38B8: LD A,(HL) ; Get a character from the BASIC line. + RST #28 ; Advance past a floating point number, if present. + DEFW NUMBER ; #18B6. + + CP #0D ; Is the character an 'ENTER'? + JR Z,L38C5 ; Jump if so to examine the next line. + + CALL L390E ; Parse the line, renumbering any tokens that may be followed by a line number. + JR L38B8 ; Repeat for all remaining character until end of the line. + +L38C5: LD DE,(N_STR1+4) ; #5B6B. DE=Address of the next BASIC line. + LD HL,(#5C4B) ; VARS. Fetch the address of the end of the BASIC program. + AND A ; + SBC HL,DE ; Has the end of the BASIC program been reached? + EX DE,HL ; HL=Address of start of the current BASIC line. + JR NZ,L38AA ; Jump back if not to examine the next line. + +;The end of the BASIC program has been reached so now it is time to update +;the line numbers and line lengths. + + CALL L3A05 ; DE=Count of the number of BASIC lines. + LD B,D ; + LD C,E ; BC=Count of the number of BASIC lines. + LD DE,#0000 ; + LD HL,(#5C53) ; PROG. HL=Address of first BASIC line. + +L38DD: PUSH BC ; BC=Count of number of lines left to update. + PUSH DE ; DE=Index of the current line. + + PUSH HL ; HL=Address of current BASIC line. + + LD HL,(RNSTEP) ; #5B96. HL=Renumber line increment. + RST #28 ; Calculate new line number offset, i.e. Line increment * Line index. + DEFW HL_MULT_DE ; #30A9. HL=HL*DE in ROM 1. + LD DE,(RNFIRST) ; #5B94. The initial line number when renumbering. + ADD HL,DE ; HL=The new line number for the current line. + EX DE,HL ; DE=The new line number for the current line. + + POP HL ; HL=Address of current BASIC line. + + LD (HL),D ; Store the new line number for this line. + INC HL ; + LD (HL),E ; + INC HL ; + LD C,(HL) ; Fetch the line length. + INC HL ; + LD B,(HL) ; + INC HL ; + ADD HL,BC ; Point to the next line. + + POP DE ; DE=Index of the current line. + INC DE ; Increment the line index. + + POP BC ; BC=Count of number of lines left to update. + DEC BC ; Decrement counter. + LD A,B ; + OR C ; + JR NZ,L38DD ; Jump back while more lines to update. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + LD (RNLINE),BC ; #5B92. Clear the address of line length bytes of the 'current line being renumbered'. + ; [No need to clear this] + SCF ; Signal not to produce an error beep. + RET ; + +; ------------------------- +; Tokens Using Line Numbers +; ------------------------- +; A list of all tokens that maybe followed by a line number and hence +; require consideration. + +L3907: DEFB #CA ; 'LINE'. + DEFB #F0 ; 'LIST'. + DEFB #E1 ; 'LLIST'. + DEFB #EC ; 'GO TO'. + DEFB #ED ; 'GO SUB'. + DEFB #E5 ; 'RESTORE'. + DEFB #F7 ; 'RUN'. + +; ----------------------------------------------- +; Parse a Line Renumbering Line Number References +; ----------------------------------------------- +; This routine examines a BASIC line for any tokens that may be followed by a line number reference +; and if one is found then the new line number if calculated and substituted for the old line number +; reference. Although checks are made to ensure an out of memory error does not occur, the routine +; simply returns silently in such scenarios and the renumber routine will continue onto the next BASIC +; line. +; Entry: HL=Address of current character in the current BASIC line. +; A=Current character. + +L390E: INC HL ; Point to the next character. + LD (HD_11+1),HL ; #5B79. Store it. + + EX DE,HL ; DE=Address of next character. + LD BC,#0007 ; There are 7 tokens that may be followed by a line + LD HL,L3907 ; number, and these are listed in the table at #3907 (ROM 0). + CPIR ; Search for a match for the current character. + EX DE,HL ; HL=Address of next character. + RET NZ ; Return if no match found. + +;A token that might be followed by a line number was found. If it is followed by a +;line number then proceed to renumber the line number reference. Note that the statements +;such as GO TO VAL "100" will not be renumbered. The line numbers of each BASIC line will +;be renumbered as the last stage of the renumber process at #38D2 (ROM 0). + + LD C,#00 ; Counts the number of digits in the current line number representation. + ; B will be #00 from above. + +L391F: LD A,(HL) ; Fetch the next character. + CP ' ' ; #20. Is it a space? + JR Z,L393F ; Jump ahead if so to parse the next character. + + RST #28 ; + DEFW NUMERIC ; #2D1B. Is the character a numeric digit? + JR NC,L393F ; Jump if a numeric digit to parse the next character. + + CP '.' ; #2E. Is it a decimal point? + JR Z,L393F ; Jump ahead if so to parse the next character. + + CP #0E ; Does it indicate a hidden number? + JR Z,L3943 ; Jump ahead if so to process it. + + OR #20 ; Convert to lower case. + CP 'e' ; #65. Is it an exponent 'e'? + JR NZ,L393B ; Jump if not to parse the next character. + + LD A,B ; Have any digits been found? + OR C ; + JR NZ,L393F ; Jump ahead to parse the next character. + +;A line number reference was not found + +L393B: LD HL,(HD_11+1) ; #5B79. Retrieve the address of the next character. + RET ; + +L393F: INC BC ; Increment the number digit counter. + INC HL ; Point to the next character. + JR L391F ; Jump back to parse the character at this new address. + +;An embedded number was found + +L3943: LD (HD_00),BC ; #5B71. Note the number of digits in the old line number reference. + + PUSH HL ; Save the address of the current character. + + RST #28 ; + DEFW NUMBER ; #18B6. Advance past internal floating point representation, if present. + + CALL L3A36 ; Skip over any spaces. + + LD A,(HL) ; Fetch the new character. + POP HL ; HL=Address of the current character. + CP ':' ; #3A. Is it ':'? + JR Z,L3957 ; Jump if so. + + CP #0D ; Is it 'ENTER'? + RET NZ ; Return if not. + +;End of statement/line found + +L3957: INC HL ; Point to the next character. + + RST #28 ; + DEFW STACK_NUM ; #33B4. Move floating point number to the calculator stack. + RST #28 ; + DEFW FP_TO_BC ; #2DA2. Fetch the number line to BC. [*BUG* - This should test the carry flag to check whether + ; the number was too large to be transferred to BC. If so then the line number should be set to 9999, + ; as per the instructions at #396A (ROM 0). As a result, the call the LINE_ADDR below can result in a crash. + ; The bug can be resolved using a JR C,#396A (ROM 0) instruction. Credit: Ian Collier (+3), Andrew Owen (128)] + LD H,B ; + LD L,C ; Transfer the number line to HL. + + RST #28 ; Find the address of the line number specified by HL. + DEFW LINE_ADDR ; #196E. HL=Address of the BASIC line, or the next one if it does not exist. + JR Z,L396F ; Jump if the line exists. + + LD A,(HL) ; Has the end of the BASIC program been reached? + CP #80 ; [*BUG* - This tests for the end of the variables area and not the end of the BASIC program area. Therefore, + ; the renumber routine will not terminate properly if variables exist in memory when it is called. + ; Executing CLEAR prior to renumbering will overcome this bug. + ; It can be fixed by replacing CP #80 with the instructions AND #C0 / JR Z,#396F (ROM 0). Credit: Ian Collier (+3), Andrew Owen (128)] + JR NZ,L396F ; Jump ahead if not. + + LD HL,#270F ; Make the reference point to line 9999. + JR L3980 ; Jump ahead to update the reference to use the new line number. + +;The reference line exists + +L396F: LD (HD_0F+1),HL ; #5B77. Store the address of the referenced line. + CALL L3A0B ; DE=Count of the number of BASIC lines up to the referenced line. + LD HL,(RNSTEP) ; #5B96. Fetch the line number increment. + RST #28 ; + DEFW HL_MULT_DE ; #30A9. HL=HL*DE in ROM 1. HL=Number of lines * Line increment = New referenced line number. + ; [An overflow could occur here and would not be detected. The code at #3898 (ROM 0) + ; should have trapped that such an overflow would occur and hence there would have been + ; no possibility of it occurring here.] + LD DE,(RNFIRST) ; #5B94. Starting line number for Renumber. + ADD HL,DE ; HL=New referenced line number. + +;HL=New line number being referenced + +L3980: LD DE,HD_0B+1 ; #5B73. Temporary buffer to generate ASCII representation of the new line number. + PUSH HL ; Save the new line number being referenced. + CALL L3A3C ; Create the ASCII representation of the line number in the buffer. + + LD E,B ; + INC E ; + LD D,#00 ; DE=Number of digits in the new line number. + + PUSH DE ; DE=Number of digits in the new line number. + PUSH HL ; HL=Address of the first non-'0' character in the buffer. + + LD L,E ; + LD H,#00 ; HL=Number of digits in the new line number. + LD BC,(HD_00) ; #5B71. Fetch the number of digits in the old line number reference. + OR A ; + SBC HL,BC ; Has the number of digits changed? + LD (HD_00),HL ; #5B71. Store the difference between the number of digits in the old and new line numbers. + JR Z,L39CF ; Jump if they are the same length. + + JR C,L39C5 ; Jump if the new line number contains less digits than the old. + +;The new line number contains more digits than the old line number + + LD B,H ; + LD C,L ; BC=Length of extra space required for the new line number. + LD HL,(HD_11+1) ; #5B79. Fetch the start address of the old line number representation within the BASIC line. + PUSH HL ; Save start address of the line number reference. + PUSH DE ; DE=Number of non-'0' characters in the line number string. + + LD HL,(#5C65) ; STKEND. Fetch the start of the spare memory. + ADD HL,BC ; Would a memory overflow occur if the space were created? + JR C,L39BE ; Jump if not to return without changing the line number reference. + + EX DE,HL ; DE=New STKEND address. + LD HL,#0082 ; Would there be at least 130 bytes at the top of RAM? + ADD HL,DE ; + JR C,L39BE ; Jump if not to return without changing the line number reference. + + SBC HL,SP ; Is the new STKEND address below the stack? + CCF ; + JR C,L39BE ; Jump if not to return without changing the line number reference. + + POP DE ; DE=Number of non-'0' characters in the line number string. + POP HL ; HL=Start address of line number reference. + RST #28 ; + DEFW MAKE_ROOM ; #1655. Create the space for the extra line number digits. + JR L39CF ; Jump ahead to update the number digits. + +;No room available to insert extra line number digits + +L39BE: POP DE ; Discard stacked items. + POP HL ; + +; [At this point the stack contains 3 surplus items. These are not explicitly popped off the stack since the call to #1F45 (ROM 0) will restore +; the stack to the state it was in at #3888 (ROM 0) when the call to #1F20 (ROM 0) saved it.] + +;Exit if no BASIC program, renumbering would cause a line number overflow or renumbering would cause an out of memory condition + +L39C0: CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + AND A ; Reset the carry flag so that an error beep will be produced. + RET ; + +;The new line number contains less digits than the old line number + +L39C5: DEC BC ; BC=Number of digits in the old line number reference. + DEC E ; Decrement number of digits in the new line number. + JR NZ,L39C5 ; Repeat until BC has been decremented by the number of digits in the new line number, + ; thereby leaving BC holding the number of digits in the BASIC line to be discarded. + + LD HL,(HD_11+1) ; #5B79. Fetch the start address of the old line number representation within the BASIC line. + RST #28 ; + DEFW RECLAIM_2 ; #19E8. Discard the redundant bytes. + +;The appropriate amount of space now exists in the BASIC line so update the line number value + +L39CF: LD DE,(HD_11+1) ; #5B79. Fetch the start address of the old line number representation within the BASIC line. + POP HL ; HL=Address of the first non-'0' character in the buffer. + POP BC ; BC=Number of digits in the new line number. + LDIR ; Copy the new line number into place. + + EX DE,HL ; HL=Address after the line number text in the BASIC line. + LD (HL),#0E ; Store the hidden number marker. + + POP BC ; Retrieve the new line number being referenced. + INC HL ; HL=Address of the next position within the BASIC line. + PUSH HL ; + + RST #28 ; + DEFW STACK_BC ; #2D2B. Put the line number on the calculator stack, returning HL pointing to it. + ; [*BUG* - This stacks the new line number so that the floating point representation can be copied. + ; However, the number is not actually removed from the calculator stack. Therefore the + ; amount of free memory reduces by 5 bytes as each line with a line number reference is renumbered. + ; A call to FP_TO_BC (at #2DA2 within ROM 1) after the floating point form has been copied would fix + ; the bug. Note that all leaked memory is finally reclaimed when control is returned to the Editor but the + ; bug could prevent large programs from being renumbered. Credit: Paul Farrow] + POP DE ; DE=Address of the next position within the BASIC line. + LD BC,#0005 ; + LDIR ; Copy the floating point form into the BASIC line. + EX DE,HL ; HL=Address of character after the newly inserted floating point number bytes. + PUSH HL ; + + LD HL,(RNLINE) ; #5B92. HL=Address of the current line's length bytes. + PUSH HL ; + + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Existing length of the current line. + LD HL,(HD_00) ; #5B71. HL=Change in length of the line. + ADD HL,DE ; + EX DE,HL ; DE=New length of the current line. + + POP HL ; HL=Address of the current line's length bytes. + LD (HL),E ; + INC HL ; + LD (HL),D ; Store the new length. + + LD HL,(N_STR1+4) ; #5B6B. HL=Address of the next BASIC line. + LD DE,(HD_00) ; #5B71. DE=Change in length of the current line. + ADD HL,DE ; + LD (N_STR1+4),HL ; #5B6B. Store the new address of the next BASIC line. + + POP HL ; HL=Address of character after the newly inserted floating point number bytes. + RET ; + +; ------------------------------- +; Count the Number of BASIC Lines +; ------------------------------- +; This routine counts the number of lines in the BASIC program, or if entered at #3A0B (ROM 0) counts +; the number of lines up in the BASIC program to the address specified in HD_0F+1. +; Exit: DE=Number of lines. + +L3A05: LD HL,(#5C4B) ; VARS. Fetch the address of the variables + LD (HD_0F+1),HL ; #5B77. and store it. + +L3A0B: LD HL,(#5C53) ; PROG. Fetch the start of the BASIC program + LD DE,(HD_0F+1) ; #5B77. and compare against the address of + OR A ; the end address to check whether there is + SBC HL,DE ; a BASIC program. + JR Z,L3A31 ; Jump if there is no BASIC program. + + LD HL,(#5C53) ; PROG. Fetch the start address of the BASIC program. + LD BC,#0000 ; A count of the number of lines. + +L3A1D: PUSH BC ; Save the line number count. + + RST #28 ; Find the address of the next BASIC line from the + DEFW NEXT_ONE ; #19B8. location pointed to by HL, returning it in DE. + + LD HL,(HD_0F+1) ; #5B77. Fetch the start of the variables area, + AND A ; i.e. end of the BASIC program. + SBC HL,DE ; + JR Z,L3A2E ; Jump if end of BASIC program reached. + + EX DE,HL ; HL=Address of current line. + + POP BC ; Retrieve the line number count. + INC BC ; Increment line number count. + JR L3A1D ; Jump back to look for the next line. + +L3A2E: POP DE ; Retrieve the number of BASIC lines and + INC DE ; increment since originally started on a line. + RET ; + +;No BASIC program + +L3A31: LD DE,#0000 ; There are no BASIC lines. + RET ; + +; ----------- +; Skip Spaces +; ----------- + +L3A35: INC HL ; Point to the next character. + +L3A36: LD A,(HL) ; Fetch the next character. + CP ' ' ; #20. Is it a space? + JR Z,L3A35 ; Jump if so to skip to next character. + + RET ; + +; --------------------------------------- +; Create ASCII Line Number Representation +; --------------------------------------- +; Creates an ASCII representation of a line number, replacing leading +; zeros with spaces. +; Entry: HL=The line number to convert. +; DE=Address of the buffer to build ASCII representation in. +; B=Number of non-'0' characters minus 1 in the ASCII representation. +; Exit : HL=Address of the first non-'0' character in the buffer. + +L3A3C: PUSH DE ; Store the buffer address. + + LD BC,#FC18 ; BC=-1000. + CALL L3A60 ; Insert how many 1000s there are. + LD BC,#FF9C ; BC=-100. + CALL L3A60 ; Insert how many 100s there are. + LD C,#F6 ; BC=-10. + CALL L3A60 ; Insert how many 10s there are. + LD A,L ; A=Remainder. + ADD A,'0' ; #30. Convert into an ASCII character ('0'..'9'). + LD (DE),A ; Store it in the buffer. + INC DE ; Point to the next buffer position. + +; Now skip over leading zeros + + LD B,#03 ; Skip over 3 leading zeros at most. + POP HL ; Retrieve the buffer start address. + +L3A56: LD A,(HL) ; Fetch a character. + CP '0' ; #30. Is it a leading zero? + RET NZ ; Return as soon as a non-'0' character is found. + + LD (HL),' ' ; #20. Replace it with a space. + INC HL ; Point to the next buffer location. + DJNZ L3A56 ; Repeat until all leading zeros removed. + + RET ; + +; ------------------------ +; Insert Line Number Digit +; ------------------------ +; This routine effectively works out the result of HL divided by BC. It does this by +; repeatedly adding a negative value until no overflow occurs. +; Entry: HL=Number to test. +; BC=Negative amount to add. +; DE=Address of buffer to insert ASCII representation of the number of divisions. +; Exit : HL=Remainder. +; DE=Next address in the buffer. + +L3A60: XOR A ; Assume a count of 0 additions. + +L3A61: ADD HL,BC ; Add the negative value. + INC A ; Increment the counter. + JR C,L3A61 ; If no overflow then jump back to add again. + + SBC HL,BC ; Undo the last step + DEC A ; and the last counter increment. + + ADD A,'0' ; #30. Convert to an ASCII character ('0'..'9'). + LD (DE),A ; Store it in the buffer. + INC DE ; Point to the next buffer position. + RET ; + + +; ======================== +; EDITOR ROUTINES - PART 4 +; ======================== + +; ------------------------------------ +; Initial Lower Screen Cursor Settings +; ------------------------------------ +; Copied to #FD6C-#FD73. + +L3A6D: DEFB #08 ; Number of bytes in table. + DEFB #00 ; #FD6C. [Setting never used] + DEFB #00 ; #FD6D = Rows above the editing area. + DEFB #14 ; #FD6E. [Setting never used] + DEFB #00 ; #FD6F. [Setting never used] + DEFB #00 ; #FD70. [Setting never used] + DEFB #00 ; #FD71. [Setting never used] + DEFB #0F ; #FD72 = Cursor attribute colour (blue paper, white ink). + DEFB #00 ; #FD73 = Stored cursor position screen attribute colour (None = black paper, black ink). + +; ----------------------------------- +; Initial Main Screen Cursor Settings +; ----------------------------------- +; Copied to #FD6C-#FD73. + +L3A76: DEFB #08 ; Number of bytes in table. + DEFB #00 ; #FD6C. [Setting never used] + DEFB #16 ; #FD6D = Rows above the editing area. + DEFB #01 ; #FD6E. [Setting never used] + DEFB #00 ; #FD6F. [Setting never used] + DEFB #00 ; #FD70. [Setting never used] + DEFB #00 ; #FD71. [Setting never used] + DEFB #0F ; #FD72 = Cursor attribute colour (blue paper, white ink). + DEFB #00 ; #FD73 = Stored cursor position screen attribute colour (None = black paper, black ink). + +; -------------------------------------- +; Set Main Screen Editing Cursor Details +; -------------------------------------- +; Set initial cursor editing settings when using the main screen. +; Copies 8 bytes from #3A6E-#3A75 (ROM 0) to #FD6C-#FD73. + +L3A7F: LD IX,#FD6C ; Point IX at cursor settings in workspace. + + LD HL,L3A6D ; Initial values table for the lower screen cursor settings. + JR L3A8B ; Jump ahead. + +; --------------------------------------- +; Set Lower Screen Editing Cursor Details +; --------------------------------------- +; Set initial cursor editing settings when using the lower screen. +; Copies 8 bytes from #3A77-#3A7E (ROM 0) to #FD6C-#FD73. + +L3A88: LD HL,L3A76 ; Initial values table for the main screen cursor settings. + +L3A8B: LD DE,#FD6C ; DE=Cursor settings in workspace. + JP L3FBA ; Jump to copy the settings. + + +; ======================== +; UNUSED ROUTINES - PART 2 +; ======================== + +; ---------- +; Print 'AD' +; ---------- +; This routine prints to the current channel the contents of register A and then the contents of register D. +; +; [Never called by ROM]. + +L3A91: RST #10 ; Print character held in A. + LD A,D ; + RST #10 ; Print character held in D. + SCF ; + RET ; + + +; ======================== +; EDITOR ROUTINES - PART 5 +; ======================== + +; ------------------- +; Store Cursor Colour +; ------------------- +; Entry: A=Cursor attribute byte. +; IX=Address of the cursor settings information. + +L3A96: AND #3F ; Mask off flash and bright bits. + LD (IX+#06),A ; Store it as the new cursor attribute value. + SCF ; + RET ; + +; ----------------------------- +; Set Cursor Position Attribute +; ----------------------------- +; Entry: B=Row number +; C=Column number. +; IX=Address of the cursor settings information. + +L3A9D: LD A,(IX+#01) ; A=Rows above the editing area (#16 when using the lower screen, #00 when using the main screen). + ADD A,B ; B=Row number within editing area. + LD B,A ; B=Screen row number. + CALL L3BA0 ; Get address of attribute byte into HL. + + LD A,(HL) ; Fetch current attribute byte. + LD (IX+#07),A ; Store the current attribute byte. + CPL ; Invert colours. + AND #C0 ; Mask off flash and bright bits. + OR (IX+#06) ; Get cursor colour. + LD (HL),A ; Store new attribute value to screen. + + SCF ; [Redundant since calling routine preserves AF] + RET ; + +; --------------------------------- +; Restore Cursor Position Attribute +; --------------------------------- +; Entry: B=Row number +; C=Column number. +; IX=Address of the cursor settings information. + +L3AB2: LD A,(IX+#01) ; A=Rows above the editing area (#16 when using the lower screen, #00 when using the main screen). + ADD A,B ; B=Row number within editing area. + LD B,A ; B=Screen row number. + CALL L3BA0 ; Get address of attribute byte into HL. + LD A,(IX+#07) ; Get previous attribute value. + LD (HL),A ; Set colour. + RET ; + +; ---------------------------------- +; Shift Up Edit Rows in Display File +; ---------------------------------- +; This routine shifts edit rows in the display file up, replacing the bottom row with the +; top entry from the Below-Screen Line Edit Buffer. +; Entry: HL=Address of first row in the Below-Screen Line Edit Buffer. +; E =Number of editing rows on screen. +; B =Row number to shift from. + +L3ABF: PUSH HL ; Save the address of the Below-Screen Line Edit Buffer row. + + LD H,#00 ; Indicate to shift rows up. + LD A,E ; A=Number of editing rows on screen. + SUB B ; A=Number of rows to shift, i.e. from current row to end of edit screen. + JR L3ACD ; Jump ahead. + +; ------------------------------------ +; Shift Down Edit Rows in Display File +; ------------------------------------ +; This routine shifts edit rows in the display file down, replacing the top row with the +; bottom entry from the Above-Screen Line Edit Buffer. +; Entry: HL=Address of next row to use within the Above-Screen Line Edit Buffer. +; E =Number of editing rows on screen. +; B =Row number to shift from. + +L3AC6: PUSH HL ; Save the address of the first row in Below-Screen Line Edit Buffer. + + LD A,E ; A=Number of editing rows on screen. + LD E,B ; E=Row number to shift from. + LD B,A ; B=Number of editing rows on screen. + SUB E ; A=Number of rows to shift, i.e. from current row to end of edit screen. + LD H,#FF ; Indicate to shift rows down. + +; Shift Rows +; ---------- + +L3ACD: LD C,A ; C=Number of rows to shift. + + LD A,B ; A=Row number to shift from. + CP E ; Is it the final row of the editing screen? + JR Z,L3B1D ; Jump if so to simply display the row. + +;Shift all display file and attributes rows up + + PUSH DE ; Save number of editing rows on screen, in E. + CALL L3B98 ; B=Inverted row number, i.e. 24-row number. + +L3AD6: PUSH BC ; B=Inverted row number, C=Number of rows left to shift. + LD C,H ; Store the direction flag. + + RST #28 ; + DEFW CL_ADDR ; #0E9B. HL=Destination display file address, for the row number specified by 24-B. + EX DE,HL ; DE=Destination display file address. + + XOR A ; + OR C ; Fetch the direction flag. + JR Z,L3AE3 ; Jump if moving up to the previous row. + + INC B ; Move to the previous row (note that B is inverted, i.e. 24-row number). + JR L3AE4 ; Jump ahead. + +L3AE3: DEC B ; Move to the next row (note that B is inverted, i.e. 24-row number). + +L3AE4: PUSH DE ; DE=Destination display file address. + RST #28 ; + DEFW CL_ADDR ; #0E9B. HL=Source display file address, for the row number held in B. + POP DE ; DE=Destination display file address. + +;Copy one row of the display file + + LD A,C ; Fetch the direction flag. + LD C,#20 ; 32 columns. + LD B,#08 ; 8 lines. + +L3AEE: PUSH BC ; + PUSH HL ; + PUSH DE ; + LD B,#00 ; + LDIR ; Copy one line in the display file. + POP DE ; + POP HL ; + POP BC ; + + INC H ; Next source line in the display file. + INC D ; Next destination line in the display file. + DJNZ L3AEE ; Repeat for all lines in the row. + +;Copy one row of display attributes + + PUSH AF ; Save the duration flag. + PUSH DE ; DE=Address of next destination row in the display file. + + RST #28 ; HL=Address of next source row in the display file. + DEFW CL_ATTR ; #0E88. DE=Address of corresponding attribute cell. + EX DE,HL ; HL=Address of corresponding source attribute cell. + EX (SP),HL ; Store source attribute cell on the stack, and fetch the next destination row in the display file in HL. + + RST #28 ; HL=Address of next destination row in the display file. + DEFW CL_ATTR ; #0E88. DE=Address of corresponding destination attribute cell. + EX DE,HL ; HL=Address of corresponding destination attribute cell. + EX (SP),HL ; Store destination attribute cell on the stack, and fetch the source attribute cell in HL. + POP DE ; DE=Destination attribute cell. + + LD BC,#0020 ; + LDIR ; Copy one row of the attributes file. + +;Repeat to shift the next row + + POP AF ; Retrieve the direction flag. + POP BC ; B=Inverted row number, C=Number of rows left to shift. + AND A ; Shifting up or down? + JR Z,L3B16 ; Jump if shifting rows up. + + INC B ; Move to the previous row, i.e. the row to copy (note that B is inverted, i.e. 24-row number). + JR L3B17 ; Jump ahead. + +L3B16: DEC B ; Move to the next row, i.e. the row to copy (note that B is inverted, i.e. 24-row number). + +L3B17: DEC C ; Decrement the row counter. + LD H,A ; H=Direction flag. + JR NZ,L3AD6 ; Jump if back more rows to shift. + + POP DE ; E=Number of editing rows on screen. + LD B,E ; B=Number of editing rows on screen. + +L3B1D: POP HL ; HL=Address of the Line Edit Buffer row to print (either in the Above-Screen Line Edit Buffer or in the Below-Screen Line Edit Buffer). + +; -------------------------------------------- +; Print a Row of the Edit Buffer to the Screen +; -------------------------------------------- +; This routine prints all 32 characters of a row in the edit buffer to the display file. +; When shifting all rows up, this routine prints the top entry of the Below-Screen Line Edit Buffer +; to the first row of the display file. +; When shifting all rows down, this routine prints the bottom entry of the Above-Screen Line Edit Buffer +; to the last editing row of the display file. +; Entry: B =Row number to print at. +; HL=Address of edit buffer row to print. + +L3B1E: CALL L3BB8 ; Exchange colour items. + + EX DE,HL ; Transfer address of edit buffer row to DE. + + LD A,(#5C3C) ; TVFLAG. + PUSH AF ; + LD HL,#EC0D ; Editor flags. + BIT 6,(HL) ; Test the editing area flag. + RES 0,A ; Allow leading space. + JR Z,L3B31 ; Jump if editing area is the main screen. + + SET 0,A ; Suppress leading space. + +L3B31: LD (#5C3C),A ; TVFLAG. + + LD C,#00 ; The first column position of the edit row. + CALL L372B ; Print AT. + + EX DE,HL ; HL=Address of edit buffer row. + LD B,#20 ; 32 columns. + +L3B3C: LD A,(HL) ; Character present in this position? + AND A ; + JR NZ,L3B42 ; Jump if character found. + + LD A,#20 ; Display a space for a null character. + +L3B42: CP #90 ; Is it a single character or UDG? + JR NC,L3B55 ; Jump if it is a UDG. + + RST #28 ; Print the character. + DEFW PRINT_A_1 ; #0010. + +L3B49: INC HL ; + DJNZ L3B3C ; Repeat for all column positions. + + POP AF ; Restore original suppress leading space status. + LD (#5C3C),A ; TVFLAG. + + CALL L3BB8 ; Exchange colour items. + SCF ; [Redundant since never subsequently checked] + RET ; + +L3B55: CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + RST #10 ; Print it (need to page in RAM bank 0 to allow access to UDGs). + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + JR L3B49 ; Jump back for next character. + +; ------------------ +; Clear Display Rows +; ------------------ +; Entry: B=Top row to clear from. +; D=Bottom row to clear to. + +L3B5E: CALL L3BB8 ; Exchange 48 and 128 editing colour items. + + LD A,D ; Bottom row to clear. + SUB B ; + INC A ; A=Number of rows to clear. + LD C,A ; C=Number of rows to clear. + CALL L3B98 ; B=Number of rows to end of screen. + +;Clear display file row + +L3B68: PUSH BC ; B=Row number. C=Row to clear. + + RST #28 ; + DEFW CL_ADDR ; #0E9B. Find display file address. + + LD C,#08 ; 8 lines in the row. + +L3B6E: PUSH HL ; Save start of row address. + + LD B,#20 ; 32 columns. + XOR A ; + +L3B72: LD (HL),A ; Blank the row. + INC HL ; + DJNZ L3B72 ; + + POP HL ; Get start of row address. + INC H ; Next line. + DEC C ; + JR NZ,L3B6E ; Repeat for all rows. + + LD B,#20 ; 32 columns. + PUSH BC ; + RST #28 ; + DEFW CL_ATTR ; #0E88. Find attribute address. + EX DE,HL + POP BC ; BC=32 columns. + +;Reset display file attributes + + LD A,(#5C8D) ; ATTR_P. + +L3B86: LD (HL),A ; Set display file position attribute. + INC HL ; + DJNZ L3B86 ; Repeat for all attributes in the row. + +;Repeat for next row + + POP BC ; B=Row number. C=Number of rows to clear. + DEC B ; + DEC C ; + JR NZ,L3B68 ; Repeat for all rows. + + CALL L3BB8 ; Exchange 48 and 128 editing colour items. + SCF ; [Redundant since never subsequently checked] + RET ; + +; -------------------------------------- +; Find Rows and Columns to End of Screen +; -------------------------------------- +; This routine calculates the number of rows to the end row of the screen and +; the number of columns to the end column of the screen. It takes into account +; the number of rows above the editing area. +; Entry: B=Row number. +; C=Column number. +; Exit : B=Number of rows to end row of screen. +; C=Number of columns to the end column of the screen. + +L3B94: LD A,#21 ; Reverse column number. + SUB C ; + LD C,A ; C=33-C. Columns to end of screen. + +; -------------------------- +; Find Rows to End of Screen +; -------------------------- +; This routine calculates the number of rows to the end row of the screen. It +; takes into account the number of rows above the editing area. +; Entry: B=Row number. +; Exit : B=Number of rows to end of screen. +; IX=Address of the cursor settings information. + +L3B98: LD A,#18 ; Row 24. + SUB B ; A=24-B. + SUB (IX+#01) ; Subtract the number of rows above the editing area. + LD B,A ; B=Rows to end of screen. + RET ; + +; --------------------- +; Get Attribute Address +; --------------------- +; Get the address of the attribute byte for the character position (B,C). +; Entry: B=Row number. +; C=Column number. +; Exit : HL=Address of attribute byte. + +L3BA0: PUSH BC ; Save BC. + + XOR A ; A=0. + LD D,B ; + LD E,A ; DE=B*256. + RR D ; + RR E ; + RR D ; + RR E ; + RR D ; + RR E ; DE=B*32. + LD HL,#5800 ; Start of attributes file. + LD B,A ; B=0. + ADD HL,BC ; Add column offset. + ADD HL,DE ; Add row offset. + + POP BC ; Restore BC. + RET ; + +; --------------------- +; Exchange Colour Items +; --------------------- +; Exchange 128 Editor and main colour items. + +L3BB8: PUSH AF ; Save registers. + PUSH HL ; + PUSH DE ; + + LD HL,(#5C8D) ; ATTR_P, MASK_P. Fetch main colour items. + LD DE,(#5C8F) ; ATTR_T, MASK_T. + EXX ; Store them. + + LD HL,(#EC0F) ; Alternate Editor ATTR_P, MASK_P. Fetch alternate Editor colour items. + LD DE,(#EC11) ; Alternate Editor ATTR_T, MASK_T. + LD (#5C8D),HL ; ATTR_P, MASK_P. Store alternate Editor colour items as main colour items. + LD (#5C8F),DE ; ATTR_T, MASK_T. + + EXX ; Retrieve main colour items ATTR_T and MASK_T. + LD (#EC0F),HL ; Alternate Editor ATTR_P, MASK_P. + LD (#EC11),DE ; Alternate Editor ATTR_T, MASK_T. Store alternate Editor colour items as main colour items. + + LD HL,#EC13 ; Alternate P_FLAG. Temporary Editor store for P_FLAG. + LD A,(#5C91) ; P_FLAG. + LD D,(HL) ; Fetch alternate Editor version. + LD (HL),A ; Store main version in alternate Editor store. + LD A,D ; A=Alternate Editor version. + LD (#5C91),A ; P_FLAG. Store it as main version. + + POP DE ; Restore registers. + POP HL ; + POP AF ; + RET ; + +;[v]..................................................................................................................... SP2000 [v] +; =================== +; TAPE TESTER ROUTINE +; =================== + +; The Tape Tester routine displays a bright blue bar completely across row 8, +; with 6 black markers evenly distributed above it on row 7 (columns 1, 7, 13, +; 19, 25 and 31). The tape port is read 2048 times and the number of highs/lows +; counted. A cyan marker is placed on the blue bar to indicate the ratio of high +; and lows. The higher the tape player volume, the further to the right the cyan +; marker will appear. The Tape Tester can be exited by pressing BREAK (though only +; SPACE checked), ENTER or EDIT (though only key 1 checked). Note that no attempt +; to read the keypad is made and so it cannot be used to exit the Tape Tester. +; +; Although the Sinclair manual suggests setting the tape player volume such that +; the cyan marker appears as far to the right of the screen as possible, this does +; not guarantee the best possible loading volume. Instead, it appears better to +; aim for the cyan marker appearing somewhere near the mid point of the blue bar. +; +; There are bugs in the Tape Tester code that can cause the cyan level marker to +; spill over onto the first column of the row below. This is most likely to occur +; when the Tape Tester is selected whilst a tape is already playing. The routine +; initially reads the state of the tape input and assumes this represents silence. +; It then monitors the tape input for 2048 samples and counts how many high levels +; appear on the tape input. Should this initial reading of the tape port not +; correspond to silence then when a true period of silence does occur it will be +; interpreted as continuous noise and hence a maximum sample count. It is a maximum +; sample count that leads to the cyan marker spilling onto the next row. + +; L3BE9: CALL L3C56 ; Signal no key press. + +; DI ; Turn interrupts off since need accurate timing. +; IN A,(#FE) ; Read tape input port (bit 5). +; AND #40 ; Set the zero flag based on the state of the input line. + +; ; [*BUG* - Ideally the input line should be read indefinitely and the routine only continue +; ; once the level has remained the same for a large number of consecutive samples. +; ; The chances of the bug occurring can be minimised by replacing the port read +; ; instructions above with the following code. Credit: Paul Farrow. +; ; +; ; LD BC,#7FFE ; Tape input port and keyboard row B to SPACE. +; ; IN A,(C) ; Read the tape input port. +; ; AND #40 ; Keep only the state of the input line. +; ; +; ;BF23_START: +; ; LD E,A ; Save the initial state of the tape input line. +; ; LD HL,#1000 ; Number of samples to monitor for changes in input line state. +; ; +; ;BF23_LOOP: +; ; IN A,(C) ; Read the keyboard and tape input port. +; ; BIT 0,A ; Test for SPACE (i.e. BREAK). +; ; JP Z,#3C56 (ROM 0) ; Exit Tape Tester if SPACE/BREAK pressed. +; ; +; ; AND #40 ; Keep only the state of the input line. +; ; CP E ; Has the line input state changed? +; ; JR NZ,BF23_START ; Jump if so to restart the sampling procedure. +; ; +; ; DEC HL ; Decrement the number of samples left to test. +; ; LD A,H ; +; ; OR L ; +; ; JR NZ,BF23_LOOP ; Jump if more samples to test. +; ; +; ; LD A,E ; Fetch the input line state.] + +; EX AF,AF' ; Save initial state of the tape input. + +; ;Print 6 black attribute square across row 7, at 6 column intervals. + +; LD HL,#58E1 ; Screen attribute position (7,1). +; LD DE,#0006 ; DE=column spacing of the black squares. +; LD B,E ; Count 6 black squares. +; LD A,D ; A=Flash 0, Bright 0, Paper black, Ink black. + +; L3BFA: LD (HL),A ; Set a black square. +; ADD HL,DE ; Move to next column position. +; DJNZ L3BFA ; Repeat for all 6 black squares. + +; ;Now enter the main loop checking for the tape input signal + +; L3BFE: LD HL,#0000 ; Count of the number of high signals read from the tape port. +; LD DE,#0800 ; Read 2048 tape samples. [*BUG* - This should be #07C0 so that the maximum sample count corresponds +; ; to column 31 and not column 32, and hence a spill over onto the following row. Credit: Paul Farrow] + +; L3C04: LD BC,#BFFE ; Read keyboard row H to ENTER. +; IN A,(C) ; +; BIT 0,A ; Test for ENTER. +; JR Z,L3C56 ; Jump to exit Tape Tester if ENTER pressed. + +; LD B,#7F ; Read keyboard row B to SPACE. +; IN A,(C) ; +; BIT 0,A ; Test for SPACE (i.e. BREAK). +; JR Z,L3C56 ; Jump to exit Tape Tester if SPACE/BREAK pressed. + +; LD B,#F7 ; Read keyboard row 1 to 5. +; IN A,(C) ; +; BIT 0,A ; Test for 1 (i.e. EDIT). +; JR Z,L3C56 ; Jump to exit Tape Tester if 1/EDIT pressed. + +; L3C1D: DEC DE ; Decrement sample counter. +; LD A,D ; +; OR E ; +; JR Z,L3C2B ; Zero flag set if all samples read. + +; IN A,(#FE) ; +; AND #40 ; Read the tape port. +; JR Z,L3C1D ; If low then continue with next sample. + +; INC HL ; Tape port was high so increment high signal counter. +; JR L3C1D ; Continue with next sample. + +; L3C2B: RL L ; HL could hold up to #0800. +; RL H ; +; RL L ; +; RL H ; HL=HL*4. HL could now hold #0000 to #2000. + +; EX AF,AF' ; Retrieve initial state of the tape port. +; JR Z,L3C3D ; This dictates how to interpret the number of high signals counted. + +; ;If the initial tape port level was high then invert the count in H, i.e. determine number of low signals. +; ;Note that if H holds #00 then the following code will result in a column position for the cyan marker of 32, +; ;and hence it will appear in the first column of the row below. + +; EX AF,AF' ; Re-store initial state of the tape port. +; LD A,#20 ; A=Column 32. +; SUB H ; A=32-H. H could hold up to #20 so A could be #00. +; LD L,A ; L=32-H. L holds a value between #00 to #20. +; JR L3C3F ; + +; ;If the initial tape port level was low then H holds the number of high signals found. + +; L3C3D: EX AF,AF' ; Retrieve initial state of the tape port. +; LD L,H ; L holds a value between #00 to #20. + +; ;L holds the column at which to show the cyan marker. + +; L3C3F: XOR A ; +; LD H,A ; Set H to #00. +; LD DE,#591F ; Attribute position (8,31). +; LD B,#20 ; Print a blue bar 32 columns wide underneath the 6 black squares. +; ; It is drawn here so that it erases the previous cyan marker. +; LD A,#48 ; Flash 0, Bright 1, Paper blue, Ink black = Bright blue. + +; EI ; +; HALT ; Wait for the screen to be redrawn. +; DI ; + +; L3C4B: LD (DE),A ; Set each blue square in the attributes file. +; DEC DE ; Move to previous attribute position. +; DJNZ L3C4B ; Repeat for all 32 columns. + +; INC DE ; Move back to first attribute column. +; ADD HL,DE ; Determine column to show cyan marker at. + +;ISDOS: +L3BE9: +HARDWARE: + CALL L3852 ; print 'Hardware' +L3BEC: CALL PROG1 ; set flags & RET_SP + LD DE,TST2 ; командная срока + LD BC,LEN2 ; длина + JP COM_LN ; выполнение команды +;LD_48: +; LD HL,0 ; RESET to TR-DOS 48k +; PUSH HL +; LD HL,03D29h +; PUSH HL +; LD A,020h +; LD (P128_COPY),A +; JP 05B00h + +; !TODO посмотреть работает ли? +TST1: BYTE ZX_Token.randomize, ZX_Token.usr, ZX_Token.val, '"', '15616', '"' +LEN1 EQU $-TST1 + +TST2: BYTE ZX_Token.randomize, ZX_Token.usr, ZX_Token.val, '"', '15600', '"', ':', ZX_Token.rem, 'is' +LEN2 EQU $-TST2 + +PROG1: LD HL,TV_FLAGS + RES 0,(HL) + SET 6,(HL) + LD HL,#EC0E + LD (HL),#FF + JP RET_SP +LEFT_E: +LEFT: SCF + JR LRT +RIGHT_E: + AND A + CALL RIGHT + JP L2717 +RIGHT: AND A +LRT: LD HL,#EC0C + LD A,(HL) + PUSH HL + CALL L37CA + LD HL,(#F6EC) + LD A,(HL) + DEC A + DEC A + JR NC,LRT1 + XOR A +LRT1: POP HL + LD (HL),A + CALL L37CA + SCF + RET + +;***** MENU for SERVICE !!! ***** +SERVICE: + CALL L3857 ; print 'Service' + CALL PROG1 ; set flags & RET_SP + LD DE,TST2 ; командная срока + LD BC,LEN2-2 ; длина + JP COM_LN ; выполнение команды + +L3BFE EQU #3BFE +;[^]..................................................................................................................... SP2000 [^] +;!FIXIT этот кусок кода не нужен, походу. - - - - - - - - - - - - - - - - - - - - - - - - - - - [v] + LD A,#68 ; Flash 0, Bright 1, paper cyan, Ink 0 = Bright cyan. + LD (HL),A ; Show the cyan marker. + JR L3BFE ; Go back and count a new set of samples. + +;Half second delay then clear key press flag. This is called upon entry and exit of the Tape Tester. +L3C56: EI ; Re-enable interrupts. + LD B,#19 ; Count 25 interrupts. + +L3C59: HALT ; Wait for half a second. + DJNZ L3C59 ; + + LD HL,#5C3B ; FLAGS. + RES 5,(HL) ; Signal no key press + SCF ; Setting the carry flag here serves no purpose. + RET ; +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[^] + +; ======================== +; EDITOR ROUTINES - PART 5 +; ======================== + +; ------------------- +; Tokenize BASIC Line +; ------------------- +; This routine serves two purposes. The first is to tokenize a typed BASIC line into a tokenized version. The second is when a syntax error is subsequently +; detected within the tokenized line, and it is then used to search for the position within the typed line where the error marker should be shown. +; +; This routine parses the BASIC line entered by the user and generates a tokenized version in the workspace area as pointed to by system variable E_LINE. +; It suffers from a number of bugs related to the handling of '>' and '<' characters. The keywords '<>', '>=' and '<=' are the only keywords that +; do not commence with letters and the routine traps these in a different manner to all other keywords. If a '<' or '>' is encountered then it is not +; immediately copied to the BASIC line workspace since the subsequent character must be examined as it could be a '>' or '=' character and therefore +; might form the keywords '<>', '>=' or '<='. A problem occurs if the subsequent character is a letter since the parser now expects the start of a +; possible keyword. It should at this point insert the '<' or '>' into the BASIC line workspace but neglects to do this. It is only when the next non-letter +; character is encountered that the '<' or '>' gets inserted, but this is now after the previously found string has been inserted. This results the following +; types of errors: +; +; 'PRINT varA>varB' is seen by the parser as 'PRINT varAvarB>' and hence a syntax error occurs. +; 'PRINT varA>varB1' is seen by the parser as 'PRINT varAvarB>1' and hence is accepted as a valid statement. +; +; A work-around is to follow the '<' or '>' with a space since this forces the '<' or '>' to be inserted before the next potential keyword is examined. +; +; A consequence of shifting a '<' or '>' is that a line such as 'PRINT a$>b$' is seen by the parser as 'PRINT a$b$>' and so it throws a syntax error. +; The parser saved the '>' character for consideration when the next character was examined to see if it was part of the keywords '<>', '>=' or '<=', +; but fails to discard it if the end of the statement is immediately encountered. Modifying the statement to a form that will be accepted will still cause +; a syntax error since the parser mistakenly believes the '>' character applies to this statement. +; +; The parser identifies string literals contained within quotes and will not tokenize any keywords that appear inside them, except for the keywords "<>", +; "<=" and ">=" which it neglects to check for. Keywords are also not tokenized following a REM statement, except again for "<>", "<=" and ">=", until the +; end of the line is reached. This differs slightly to 48K BASIC mode. In 48K BASIC mode, typing a ':' following a REM statement will cause a change from +; 'L' cursor mode to 'K' cursor mode and hence the next key press results in a keyword token being inserted. In 128K BASIC mode, typing a ':' will not change +; to 'K' cursor mode and hence the next key press will just be the letter, number or symbol. This does not affect the running of the program since 48K BASIC +; mode will ignore all characters after a REM command until the end of the line. However, creating such a REM statement in 128K BASIC mode that appears similar +; to one created in 48K BASIC mode will result in more memory being used since the 'keyword' must be spelled out letter by letter. +; +; When being used to locate the error marker position, the same process is performed as when tokenizing but no characters are actually inserted into the workspace +; (they are still there from when the line was originally tokenized). Instead, a check is made after each character is processed to see if the error marker address +; held in system variable X_PTR has been reached. If it does match then the routine returns with BC holding the character position where the error marker should +; be displayed at. + +;Entry point - A syntax error was detected so the error marker must be located + BLOCK #3C63-$,0 +L3C63: LD A,#01 ; Signal to locate the error marker. + JR L3C69 ; Jump forward. + +;Entry point - Tokenize the BASIC line + +L3C67: LD A,#00 ; Signal to tokenize the BASIC line. [Could have saved 1 byte by using XOR A] + +L3C69: LD (#FD8A),A ; Store the 'locate error marker' flag. + + LD HL,#0000 ; + LD (#FD85),HL ; Reset count of the number of characters in the typed BASIC line being inserted. + LD (#FD87),HL ; Reset count of the number of characters in the tokenized version of the BASIC line being inserted. + ADD HL,SP ; + LD (#FD8B),HL ; Store the stack pointer. + + CALL L34EA ; Clear BASIC line construction pointers (address of next character in the Keyword Construction Buffer and the + ; address of the next character in the BASIC line within the program area being de-tokenized). + + LD A,#00 ; [Could have saved 1 byte by using XOR A] + LD (#FD84),A ; Signal last character was not a keyword and was not a space. + ; [*BUG* - Should reset the '<' and '>' store at #FD89 to #00 here. Attempting to insert a BASIC line + ; such as 'PRINT VAL a$>b' will fail since the parser does not like '>' immediately after 'a$', due to the bug at #3CB8 (ROM 0). + ; The parser stores the '>' in #FD89 since it will check the following character in case it should replace the two + ; characters with the token '<>', '>=' or '<='. After the parser throws the syntax error, it does not clear + ; #FD89 and so even if the line is modified such that it should be accepted, e.g. 'PRINT VAL a$=b', the parser + ; believes the line is really '>PRINT VAL n$=b' and so throws another syntax error. Since a letter follows the '>', + ; the contents of #FD89 will get cleared and hence a second attempt to insert the line will now succeed. Credit: Paul Farrow] + + LD HL,#FD74 ; HL=Start address of the Keyword Conversion Buffer. + LD (#FD7D),HL ; Store as the next available location. + + CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + RST #28 ; + DEFW SET_MIN ; #16B0. Clear the editing areas. + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD A,#00 ; [Could have saved 1 byte by using XOR A, or 2 bytes by clearing this above] + LD (#FD81),A ; Clear Keyword Conversion Buffer flags - not within REM, not with Quotes, no characters in the buffer. + + LD HL,(#5C59) ; E_LINE. + LD (#FD82),HL ; Store the address of the workspace for the tokenized BASIC line. + + LD HL,#0000 ; [Could have saved 1 byte by using LD H,A followed by LD L,A] + LD (#FD7F),HL ; Signal no space character between words in the Keyword Conversion Buffer. + +;Enter a loop to fetch each character from the BASIC line and insert it into the workspace, tokenizing along the way + +L3CA1: LD HL,(#FD85) ; + INC HL ; Increment count of the number of characters in the typed BASIC line. + LD (#FD85),HL ; + + CALL L3D9D ; Fetch the next character from BASIC line being inserted, return in B. + LD C,A ; Save the character status value. + +;C=#01 if not a space, not a letter, not a '#' and not a '$'. +; #02 if a '#' or '$'. +; #03 if a space. +; #06 if a letter. +;B=Character fetched. + + LD A,(#FD81) ; Have any Keyword Conversion Buffer flags been set? + CP #00 ; Has anything be put into the buffer yet? + JR NZ,L3CF4 ; Jump if so. + +;The first character to potentially put into the Keyword Conversion Buffer + +L3CB3: LD A,C ; Retrieve the character status value. + AND #04 ; Is the character a letter? + JR Z,L3CED ; Jump if not. + +;Insert the character + +L3CB8: + +; [*BUG* - At this point a '>' or '<' that was previously stored should be inserted into the BASIC line workspace. However, the routine proceeds with the new potential +; keyword and this is entered into the BASIC line workspace next. The '>' or '<' will only be inserted when the next non-letter character is encountered. This causes +; an expression such as 'a>b1' to be translated into 'ab>1'. Credit: Ian Collier (+3), Paul Farrow (128)] + +; [The bug can be fixed by testing if whether a '<' or '>' character is stored. Credit: Paul Farrow. +; +; LD A,(#FD89) ; +; AND A ; Was the last character a '>' or '<'? +; JR Z,INSERT ; Jump if not. +; +; PUSH BC ; Save the new character. +; LD B,A ; +; CALL #3E64 (ROM 0) ; Insert the '>' or '<' into the BASIC line workspace. +; POP BC ; Retrieve the new character. +; XOR A ; +; LD (#FD89),A ; Clear the '>' or '<'. +; +;INSERT: ; ] + + CALL L3DE9 ; Insert the character into the Keyword Conversion Buffer. + JR NC,L3CC4 ; Jump if no more room within the buffer, hence string is too large to be a token. + + LD A,#01 ; Signal Keyword Conversion Buffer contains characters. + LD (#FD81),A ; + JR L3CA1 ; Jump back to fetch and process the next character. + +;No room to insert the character into the Keyword Conversion Buffer hence string is too large to be a valid token + +L3CC4: LD HL,(#FD7F) ; Fetch the address of the space character between words within the Keyword Conversion Buffer. + LD A,L ; + OR H ; Is there an address set? + JP NZ,L3D1E ; Jump if so to copy the first word into the BASIC line workspace and the move the second word to the + ; start of the Keyword Conversion Buffer. Further characters can then be appended and the contents + ; re-evaluated in case a complete keyword is then available. + +;Copy the Keyword Conversion Buffer into the BASIC line workspace + +L3CCC: PUSH BC ; Save the character to insert. + CALL L3DCD ; Copy Keyword Conversion Buffer contents into BASIC line workspace. + POP BC ; Retrieve the character to insert. + + LD A,#00 ; + LD (#FD81),A ; Signal the Keyword Conversion Buffer is empty. + +;C=#01 if not a space, not a letter, not a '#' and not a '$'. +; #02 if a '#' or '$'. +; #03 if a space. +; #06 if a letter. +;B=Character fetched. + +L3CD6: LD A,C ; Retrieve the character status value. + AND #01 ; Is it a space, or not a letter and not a '#' and not a '$'? + JR NZ,L3CB3 ; Jump back if so to insert the character either into the Keyword Conversion Buffer or the BASIC line workspace. + +;The string was too long to be a keyword and was followed by a space, a '#' or a '$'. Enter a loop to insert each character of the +;string into the BASIC line workspace. + + LD A,B ; Retrieve the character to insert. + CALL L3E16 ; Insert character into BASIC line workspace. + RET NC ; Return if tokenizing is complete. + + LD HL,(#FD85) ; + INC HL ; Increment the count of the number of characters in the typed BASIC line being inserted. + LD (#FD85),HL ; + + CALL L3D9D ; Fetch the next character from BASIC line being inserted. + LD C,A ; Save the flags. + + JR L3CD6 ; Jump back to insert the character of the non-keyword string into the BASIC line workspace. + +;The character is not a letter so insert directly into the BASIC line workspace + +L3CED: LD A,B ; Retrieve the character to insert. + CALL L3E16 ; Insert character into BASIC line workspace, tokenizing '<>', '<=' and '>=' if encountered. + RET NC ; Return if tokenizing is complete. + + JR L3CA1 ; Jump back to fetch and process the next character. + +;Keyword Conversion buffer flags are set - either the buffer already contains characters, or within quotes or within a REM statement + +L3CF4: CP #01 ; Is the Keyword Conversion Buffer empty or the contents marked as being within quotes or within a REM? + JR NZ,L3CED ; Jump back if so to insert the character since this is either the first character of a new word or is within quotes or within a REM. + +;C=#01 if not a space, not a letter, not a '#' and not a '$'. +; #02 if a '#' or '$'. +; #03 if a space. +; #06 if a letter. + + LD A,C ; Retrieve the character status value. + AND #01 ; Is it a letter or a '#' or a '$'? + JR Z,L3CB8 ; Jump if so to simply insert the character. + +;The character is a space, or is not a letter and not a '#' and not a '$', i.e. the last character was the end of a potential keyword + + PUSH BC ; Save the next character to insert and the character status value. + +L3CFE: CALL L3F7E ; Attempt to identify the string in Keyword Conversion Buffer. + POP BC ; Retrieve the next character to insert and the character status value. + JR C,L3D7D ; Jump if keyword identified. + +;The string in the Keyword Conversion Buffer was not identified as a keyword + + LD HL,(#FD7F) ; Fetch the address of the space character between words within the Keyword Conversion Buffer. + LD A,H ; + OR L ; Is there an address set, i.e. a space between words? + JR NZ,L3D1E ; Jump if there is a space character. + + LD A,C ; Retrieve the character status value. + AND #02 ; Is it a space? + JR Z,L3CCC ; Jump if not to copy Keyword Conversion Buffer into the workspace since it is not a keyword. + +;Character is a space. Allow this as the keyword could be DEF FN, GO TO, GO SUB, etc. + + CALL L3DE9 ; Insert the character into the Keyword Conversion Buffer. + JR NC,L3CC4 ; Jump back if no room to insert the character, i.e. not a keyword since too large. + + LD HL,(#FD7D) ; Fetch the next location address. + DEC HL ; Point back to the last character. + LD (#FD7F),HL ; Store as the address of the space character. This is used for double keywords such as DEF FN. + JR L3CA1 ; Jump back to fetch and process the next character. + +;The string in the Keyword Conversion Buffer contains two words separated by a space that do not form a +;valid double keyword (such as DEF FN, GO SUB, GO TO, etc). +;For a BASIC line such as 'IF FLAG THEN' the Keyword Conversion Buffer holds the characters 'FLAG THEN'. +;The 'FLAG' characters get moved to the workspace and the 'THEN' characters are shifted to the start of the +;Keyword Conversion Buffer before being re-evaluated to see if they form a keyword. + +L3D1E: PUSH BC ; Save the character to insert and the character status value. + + LD HL,#FD74 ; Point to the start address of the Keyword Conversion Buffer. + LD DE,(#FD7F) ; Fetch the address of the space character between words within the Keyword Conversion Buffer. + LD A,D ; + CP H ; Is the space possibly at the start of the buffer? + JR NZ,L3D2F ; Jump if not. + + LD A,E ; + CP L ; Is the space at the start of the buffer? + JR NZ,L3D2F ; Jump if not. + + INC DE ; Point to the next location within the buffer, counter-acting the following decrement. + +L3D2F: DEC DE ; Point to the previous location within the buffer. + JR L3D33 ; Jump ahead to copy all characters to the BASIC line workspace. + +;Copy all characters from the Keyword Conversion Buffer prior to the space into the BASIC line workspace + +L3D32: INC HL ; Point to the next location within the Keyword Conversion Buffer. + +L3D33: LD A,(HL) ; Fetch a character from the Keyword Conversion Buffer. + AND #7F ; Mask off the terminator bit. + PUSH HL ; HL=Location within Keyword Conversion Buffer. + PUSH DE ; DE=Location of last character within the Keyword conversion Buffer. + CALL L3E16 ; Insert character into BASIC line workspace, including a stored '<' or '>' character. + POP DE ; + POP HL ; + + LD A,H ; + CP D ; Possibly reached the character prior to the space? + JR NZ,L3D32 ; Jump back if not to copy the next character. + + LD A,L ; + CP E ; Reached the character prior to the space? + JR NZ,L3D32 ; Jump back if not to copy the next character. + +;Now proceed to handle the next word + + LD DE,(#FD7F) ; DE=Address of the space character between words. + LD HL,#FD74 ; + LD (#FD7F),HL ; Set the address of the space character to be the start of the buffer. + + LD BC,(#FD7D) ; BC=Next location within the Keyword Conversion Buffer. + DEC BC ; Point to the last used location. + + LD A,D ; + CP H ; Is the space possibly at the start of the buffer? + JR NZ,L3D70 ; Jump if not. + + LD A,E ; + CP L ; Is the space at the start of the buffer? + JR NZ,L3D70 ; Jump if not. + +;The space character is at the start of the Keyword Conversion Buffer + + INC DE ; DE=Address after the space character within the Keyword Conversion Buffer. + PUSH HL ; HL=Start address of the Keyword Conversion Buffer. + LD HL,#0000 ; + LD (#FD7F),HL ; Signal no space character between words. + POP HL ; HL=Start address of the Keyword Conversion Buffer. + + LD A,B ; + CP H ; Is the space possibly the last character in the buffer? + JR NZ,L3D70 ; Jump if not. + + LD A,C ; + CP L ; Is the space the last character in the buffer? + JR NZ,L3D70 ; Jump if not. + + POP BC ; Retrieve the character to insert and the character status value. + JR L3D8F ; Jump ahead to continue. + +;The space is not at the start of the Keyword Conversion Buffer, i.e. the buffer contains another word after the space. +;The first word has already been copied to the BASIC line workspace so now copy the second word to the start of the Keyword Conversion Buffer +;and then see if it is a valid keyword. [It is not recommended to name a variable as per a keyword since statements such as 'PRINT then' will +;fail the syntax check since the variable 'then' is interpreted as the keyword 'THEN' and so the statement is seen as 'PRINT THEN', which in +;this case is invalid.] + +;HL points to the start of the Keyword Conversion Buffer. +;DE points to the space between the two words. + +L3D70: LD A,(DE) ; Fetch a character from the second word. + LD (HL),A ; Store it at the beginning of the buffer. + INC HL ; + INC DE ; + AND #80 ; Reached the last character in the buffer, i.e. the terminator bit set? + JR Z,L3D70 ; Jump if not to copy the next character. + + LD (#FD7D),HL ; Store the new address of the next free location. + JR L3CFE ; Jump back to attempt identification of the 'second' word as a keyword. + +;The string in the Keyword Conversion Buffer was identified as a keyword, so insert the token character code of the +;keyword into the BASIC line workspace. +;A=Character code of identified token. + +L3D7D: PUSH BC ; Save the next character to insert and the character status value. + CALL L3E16 ; Insert character held in A into BASIC line workspace. + POP BC ; Retrieve the next character to insert and the character status value. + +;The token has been inserted into the BASIC line workspace so reset the Keyword Conversion Buffer + + LD HL,#0000 ; + LD (#FD7F),HL ; Indicate no space character between words in the Keyword Conversion Buffer. + + LD A,(#FD81) ; Fetch the flag bits. + CP #04 ; Within a REM statement? + JR Z,L3D94 ; Jump if so to retain the 'within a REM' flag bit. + +L3D8F: LD A,#00 ; + LD (#FD81),A ; Signal no characters within the Keyword Conversion Buffer. + +L3D94: LD HL,#FD74 ; Start address of the Keyword Conversion Buffer. + LD (#FD7D),HL ; Store this as the next location within the buffer. + JP L3CB3 ; Jump back to insert the next character either into the Keyword Conversion Buffer or the BASIC line workspace. + +; ------------------------------------------------------------------- +; Fetch Next Character and Character Status from BASIC Line to Insert +; ------------------------------------------------------------------- +; Fetch the next character from the BASIC line being inserted and check whether a letter, a space, a '#' or a '$'. +; Exit: B=Character. +; A=#01 if not a space, not a letter, not a '#' and not a '$'. +; #02 if a '#' or '$'. +; #03 if a space. +; #06 if a letter. + +L3D9D: CALL L2D54 ; Fetch the next character from the BASIC line being inserted. + LD B,A ; Save the character. + CP '?' ; #3F. Is it below '?' (the error marker)? + JR C,L3DAF ; Jump if so. + + OR #20 ; Make lowercase. + CALL L3DC6 ; Is it a letter? + JR C,L3DC3 ; Jump if so. + +L3DAC: LD A,#01 ; Indicate not space, not letter, not '#' and not '$'. + RET ; + +L3DAF: CP #20 ; Is it a space? + JR Z,L3DC0 ; Jump if so. + + CP '#' ; #23. Is it '#'? + JR Z,L3DBD ; Jump if so. + + JR C,L3DAC ; Jump if below '#'. + + CP '$' ; #24. Is it '$'? + JR NZ,L3DAC ; Jump if not. + +L3DBD: LD A,#02 ; Indicate a '#' or '$'. + RET ; + +L3DC0: LD A,#03 ; Indicate a space. + RET ; + +L3DC3: LD A,#06 ; Indicate a letter. + RET ; + +; -------------------- +; Is Lowercase Letter? +; -------------------- +; Entry: A=Character code. +; Exit : Carry flag set is a lowercase letter. + +L3DC6: CP #7B ; Is the character above 'z'? + RET NC ; Return with carry flag reset if above 'z'. + + CP #61 ; Is the character below 'a'? + CCF ; Return with carry flag reset if below 'a'. + RET ; + +; ----------------------------------------------------------------- +; Copy Keyword Conversion Buffer Contents into BASIC Line Workspace +; ----------------------------------------------------------------- + +L3DCD: + +; [To fix the error marker bug at #3EFB (ROM 0), the code below up until the instruction at #3DDA (ROM 0) should have been as follows] +; +; LD HL,#FD74 ; Start address of the Keyword Conversion Buffer. +; CALL #3DDA (ROM 0) ; Copy all characters into the BASIC line workspace. +; LD HL,#FD74 ; Start address of the Keyword Conversion Buffer. +; LD (#FD7D),HL ; Store the next available location. +; SUB A ; A=0. +; LD (#FD7F),A ; +; LD (#FD80),A ; Signal no space character between words in the Keyword Conversion Buffer. +; RET + + LD HL,#FD74 ; Start address of the Keyword Conversion Buffer. + LD (#FD7D),HL ; Store the next available location. + SUB A ; A=0. + LD (#FD7F),A ; + LD (#FD80),A ; Signal no space character between words in the Keyword Conversion Buffer. + +L3DDA: LD A,(HL) ; Fetch a character from the buffer. + AND #7F ; Mask off the terminator bit. + + PUSH HL ; Save buffer location. + CALL L3E9C ; Insert the character into the BASIC line workspace, suppressing spaces as required. + POP HL ; Retrieve buffer location. + + LD A,(HL) ; Re-fetch the character from the buffer. + AND #80 ; Is it the terminator character? + RET NZ ; Return if so. + + INC HL ; Point to the next character in the buffer. + JR L3DDA ; Jump back to handle next buffer character. + +; ----------------------------------------------- +; Insert Character into Keyword Conversion Buffer +; ----------------------------------------------- +; Entry; B=Character to insert. +; Exit : Carry flag reset if no room to insert the character within the buffer. + +L3DE9: LD HL,(#FD7D) ; Fetch address within Keyword Conversion Buffer. + LD DE,#FD7D ; Address after Keyword Conversion Buffer. + LD A,D ; + CP H ; Has end of buffer possibly been reached? + JR NZ,L3DF8 ; Jump if not. + + LD A,E ; + CP L ; Has end of buffer been reached? + JP Z,L3E13 ; Jump if so. [Could have saved a byte by using JR instead of JP] + +;End of buffer not reached + +L3DF8: LD DE,#FD74 ; Start address of Keyword Conversion Buffer. + LD A,D ; + CP H ; Possibly at the start of the buffer? + JR NZ,L3E03 ; Jump if not. + + LD A,E ; + CP L ; At the start of the buffer? + JR Z,L3E09 ; Jump if so to simply store the character. + +;Not at the start of the buffer so need to remove terminator bit from the previous character + +L3E03: DEC HL ; Point to the last character. + LD A,(HL) ; + AND #7F ; Clear the terminator bit from the last character. + LD (HL),A ; + INC HL ; Point back at the current location. + +L3E09: LD A,B ; Retrieve the new character. + OR #80 ; Set the terminator bit. + LD (HL),A ; Store the character in the buffer. + INC HL ; Point to the next location. + LD (#FD7D),HL ; Store the address of the next location. + + SCF ; Signal character inserted. + RET ; + +;End of buffer reached + +L3E13: SCF ; + CCF ; Clear the carry flag to indicate no room to insert the character within the buffer. + RET ; + +; ---------------------------------------------------------------- +; Insert Character into BASIC Line Workspace, Handling '>' and '<' +; ---------------------------------------------------------------- +; This routine inserts a character into the BASIC line workspace, tokenizing '>=', '<=' and '<>'. +; Entry: A=Character to insert. +; Exit : If tokenizing a BASIC line then returns with carry flag reset if tokenizing is complete. +; If searching for the error marker location then returns with the carry flag set if the error marker has not been found, +; otherwise a return is made to the main calling routine with BC holding the number of characters in the typed BASIC line, +; i.e. the error marker location is at the end of the line. + +L3E16: PUSH AF ; Save the character to insert. + +; [*BUG* - The string characters "<>", "<=" and ">=" get tokenized to a single character '<>', '<=' and '>=' respectively +; even within quotes or a REM statement. Credit: Paul Collins (+3), Paul Farrow (128)] +; +; [*BUG* - 128 BASIC mode handles a colon character found following a REM statement differently to 48K mode. In 48K mode, typing +; a colon returns the cursor into 'K' mode and hence the next key press inserts a keyword token. In 128K mode, typing a colon +; does not cause the characters following it to be interpreted as a possible keyword. There is no noticeable difference when +; executing the REM statement since subsequent statements are ignored following a REM command. However, for consistency the +; 128K mode editor ought to generate identical BASIC lines to those that would be created from 48K mode. Credit: Paul Farrow] +; +; [The following instructions would be required fix the two bugs described above. Credit: Paul Farrow. +; +; LD A,(#FD81) ; +; BIT 1,A ; Within quotes? +; JR NZ,WITHIN ; Jump forward if within quotes. +; +; BIT 2,A ; Within a REM statement? +; JR Z,NOT_WITHIN ; Jump forward if not within a REM statement. +; +; POP AF ; +; PUSH AF ; +; CP ':' ; +; JR NZ,WITHIN ; Jump if not a colon. +; +; LD A,(#FD81) ; +; AND #FB ; Signal not within a REM statement. +; LD (#FD81),A ; +; +;WITHIN: +; POP AF ; Retrieve the character to insert. +; JP #3E64 (ROM 0) ; Simply insert the character into the BASIC line workspace. +; +;NOT_WITHIN: ; ] + + LD A,(#FD89) ; Was the previous character '<' or '>'? + OR A ; + JR NZ,L3E2F ; Jump if so. + + POP AF ; Retrieve the character to insert. + CP '>' ; #3E. Is it '>'? + JR Z,L3E2A ; Jump if so to store for special treatment later. + + CP '<' ; #3C. Is it '<'? + JR Z,L3E2A ; Jump if so to store for special treatment later. + +L3E26: CALL L3E64 ; Insert the character into the BASIC line workspace. + RET ; [Could have saved 1 byte by using JP #3E64 (ROM 0)] + +;The character was '<' or '>' + +L3E2A: LD (#FD89),A ; Store '<' or '>'. + SCF ; Signal tokenizing not complete or error marker not found. + RET ; + +;The previous character was '<' or '>' + +L3E2F: CP '<' ; #3C. Was the previous character '<'? + LD A,#00 ; Reset the indicator that the previous + LD (#FD89),A ; character was '<' or '>'. + JR NZ,L3E52 ; Jump ahead if not '<'. + +;Previous character was '<' + + POP AF ; Retrieve the character to insert. + CP '>' ; #3E. Is it '>'? + JR NZ,L3E41 ; Jump ahead if not. + + LD A,#C9 ; Tokenize to the single character '<>'. + JR L3E26 ; Jump back to insert the character and return. + +L3E41: CP '=' ; #3D. Is it '='? + JR NZ,L3E49 ; Jump ahead if not. + + LD A,#C7 ; Tokenize to '<='. + JR L3E26 ; Jump back to insert the character and return. + +;Previous character was '<' and new character is '<' + +L3E49: PUSH AF ; Save the current character to insert. + LD A,'<' ; #3C. + CALL L3E64 ; Put the preceding '<' character into the line. + POP AF ; Retrieve the character to insert. + JR L3E26 ; Jump back to insert the character and return. + +;Previous character was '>' + +L3E52: POP AF ; Retrieve the character to insert. + CP '=' ; #3D. Is it '='? + JR NZ,L3E5B ; Jump ahead if not. + + LD A,#C8 ; Tokenize to '>='. + JR L3E26 ; Jump back to insert the character and return. + +;Previous character was '>' and new character is '>' + +L3E5B: PUSH AF ; Save the current character to insert. + LD A,'>' ; #3E. + CALL L3E64 ; Put the preceding '>' character into the line. + POP AF ; Retrieve the character to insert. + JR L3E26 ; Jump back to insert the character and return. + +; --------------------------------------------------------------------- +; Insert Character into BASIC Line Workspace, Handling 'REM' and Quotes +; --------------------------------------------------------------------- +; This routine inserts a character into the BASIC line workspace, with special handling of a 'REM' command +; and strings contained within quotes. +; Entry: A=Character to insert. +; Exit : If tokenizing a BASIC line then returns with carry flag reset if tokenizing is complete. +; If searching for the error marker location then returns with the carry flag set if the error marker has not been found, +; otherwise a return is made directly to the main calling routine with BC holding the number of characters in the typed BASIC line, +; i.e. the error marker location is at the end of the line. + +L3E64: CP #0D ; Is it 'ENTER'? + JR Z,L3E88 ; Jump ahead if so. + + CP #EA ; Is it 'REM'? + LD B,A ; Save the character. + JR NZ,L3E74 ; Jump ahead if not REM. + +;It is a 'REM' character + + LD A,#04 ; Indicate that within a REM statement. + LD (#FD81),A ; + JR L3E82 ; Jump ahead to insert the character into the BASIC line workspace. + +L3E74: CP #22 ; Is it a quote? + JR NZ,L3E82 ; Jump ahead if not. + +;It is a quote character + + LD A,(#FD81) ; + AND #FE ; Signal last character was not a keyword. + XOR #02 ; Toggle the 'within quotes' flag. Will be 1 for an opening quote, then 0 for a closing quote. + LD (#FD81),A ; + +L3E82: LD A,B ; Retrieve the character. + CALL L3E9C ; Insert the character into the BASIC line workspace, suppressing spaces as required. + + SCF ; Indicate BASIC line tokenization not complete. + RET ; + +;It is an 'ENTER' character + +; [*BUG* - At this point a check should be made to see whether the last character was a space. If it was then it will not have +; been inserted but instead the flag in #FD84 (ROM 0) will have been set. The purpose of the flag is to filter out double +; spaces caused by the leading/trailing spaces of tokens. Only if the following character is not a space will the previous +; character, the space, be inserted. When the end of the line is found, there is no attempt to insert this space. +; The bug can be fixed by the two modifications shown below. Credit: Paul Farrow] + +L3E88: LD A,(#FD8A) ; Fetch the 'locate error marker' flag. + CP #00 ; Searching for the error marker following a syntax error? [Could have saved 1 byte by using AND A] + JR Z,L3E99 ; Jump if tokenizing the BASIC line. + +;The end of the line was reached and no error marker was found so assume the error marker exists at the end of the typed line + + LD BC,(#FD85) ; BC=Count of number of the characters in the typed BASIC line being inserted. + LD HL,(#FD8B) ; + +; [The first part of the fix for the trailing space bug is as follows: +; +; LD A,(#FD84) ; Fetch the BASIC line insertion flags. +; AND #02 ; Was the last character a space? +; JR Z,GOT_COUNT ; Jump if not. +; +; INC BC ; Increment to account for the final space. +; +;GOT_COUNT: ; ] + + LD SP,HL ; Restore the stack pointer. + SCF ; Indicate the error marker was not found within the tokenized BASIC line. + RET ; Return back to the top level calling routine, to #2D04 (ROM 0). + +;Tokenizing the BASIC line + +L3E99: + +; [The second part of the fix for the trailing space bug is as follows: +; +; LD A,(#FD84) ; Fetch the BASIC line insertion flags. +; AND #02 ; Was the last character a space? +; LD A,#20 ; Insert a space into the line. +; CALL NZ,#3EFB (ROM 0) ; If so then insert the character into the BASIC line workspace.] + + SCF ; + CCF ; Carry flag reset to indicate tokenizing complete. + RET ; + +; ----------------------------------------------------------------- +; Insert Character into BASIC Line Workspace With Space Suppression +; ----------------------------------------------------------------- +; This routine is called to insert a character into the BASIC line workspace, suppressing both leading and trailing spaces +; around tokens, e.g. 'PRINT 10' does not require a space stored between 'PRINT' and '10' within the BASIC line. +; The routine maintains two flags which indicate whether the last character was a space or was a token. Whenever a space +; is encountered, it is noted but not inserted straight away. It is only after the subsequent character is examined that +; the routine can determine whether the space should or should not be inserted. +; Entry: A=Character to insert. +; Exit : A=Updated BASIC line insertion flags. + +L3E9C: LD E,A ; Save the character to insert in E. + + LD A,(#FD84) ; + LD D,A ; D=BASIC line insertion flags. + + LD A,E ; Restore character to insert back to A. + CP #20 ; Is it a space? + JR NZ,L3EC6 ; Jump ahead if not. + +;Character to insert is a space + + LD A,D ; A=BASIC line insertion flags. + AND #01 ; Was the last character a token? + JR NZ,L3EBF ; Jump ahead if so. + + LD A,D ; A=BASIC line insertion flags. + AND #02 ; Was the last character a space? + JR NZ,L3EB7 ; Jump ahead if so. + +;Character to insert is a space and the last character was not a space/token. This could be the start of a new keyword +;so note the space but do not insert it now. + + LD A,D ; A=BASIC line insertion flags. + OR #02 ; Signal the last character was a space. + LD (#FD84),A ; Store the updated BASIC line insertion flags. + RET ; + +;Character to insert is a space and the last character was a space. The new space could be the start of a new keyword +;so keep the 'last character was a space' flag set but insert a space for the previous space that was noted. + +L3EB7: LD A,E ; Retrieve the character to insert. + CALL L3EFB ; Insert the character into the BASIC line workspace. + LD A,(#FD84) ; A=BASIC line insertion flags. + RET ; + +;Character to insert is a space and the last character was a token. Do not insert trailing spaces for tokens. + +L3EBF: LD A,D ; A=BASIC line insertion flags. + AND #FE ; Signal last character was not a token. + LD (#FD84),A ; Store the updated BASIC line insertion flags. + RET ; [Could have saved 2 bytes by using JR #3EB3 (ROM 0)] + +;Character to insert is not a space + +L3EC6: CP #A3 ; Compare against the token 'SPECTRUM' (the first 128K keyword). + JR NC,L3EEE ; Jump ahead if a token. + +;Character to insert is not a space and not a token + + LD A,D ; A=BASIC line insertion flags. + AND #02 ; Was the last character a space? + JR NZ,L3EDA ; Jump ahead if it was. + +;Character to insert is not a space and not a token and the last character inserted was not a space, so just insert the character + + LD A,D ; A=BASIC line insertion flags. + AND #FE ; Signal last character was not a keyword. + LD (#FD84),A ; Store the new flags. + + LD A,E ; Retrieve the character to insert. + CALL L3EFB ; Insert the character into the BASIC line workspace. + RET ; [Could have saved one byte by using JP #3EFB (ROM 0)] + +;Character to insert is not a space and not a token and the last character was a space. Since the new character is not a token, the previous +;space was not the start of a new keyword so insert a space and then the new character. + +L3EDA: PUSH DE ; Save the BASIC line insertion flags. + LD A,#20 ; Insert a space into the line. + CALL L3EFB ; Insert the character into the BASIC line workspace. + POP DE ; Retrieve the flags. + + LD A,D ; A=BASIC line insertion flags. + AND #FE ; Signal last character was not a keyword. + AND #FD ; Signal last character was not a space. + LD (#FD84),A ; Store the updated BASIC line insertion flags. [Could have saved 6 bytes by using JR #3ED2 (ROM 0)] + + LD A,E ; Retrieve the character to insert. + CALL L3EFB ; Insert the character into the BASIC line workspace. + RET ; + +;Character to insert is a token. Clear any previously noted space since leading spaces are not required for tokens. + +L3EEE: LD A,D ; A=BASIC line insertion flags. + AND #FD ; Signal last character was not a space. + OR #01 ; Signal last character was a keyword. + LD (#FD84),A ; Store the updated BASIC line insertion flags. [Could have saved 6 bytes by using JR #3ED2 (ROM 0)] + + LD A,E ; Retrieve the character to insert. + CALL L3EFB ; Insert the character into the BASIC line workspace. + RET ; + +; -------------------------------------------- +; Insert a Character into BASIC Line Workspace +; -------------------------------------------- +; This routine is called for two purposes. The first use is for inserting a character or token into the BASIC line workspace (situated at E_LINE). +; The second use is after a syntax error has been identified within the tokenized BASIC line in the workspace and the location of the error marker needs to be +; established. For the second case, the system variable X_PTR holds the address of where the error occurred within the tokenized BASIC line in the workspace. +; The Editor needs to identify how many characters there are before the equivalent error position is reached within the typed BASIC line. To locate it, the typed BASIC +; line is re-parsed but this time without inserting any characters into the BASIC line workspace, since this still contains the tokenized line from before. This tokenized line +; will now also include embedded floating point numbers for any numeric literals contained within the BASIC line. As the typed line is re-parsed, a count of the characters +; examined so far is kept and instead of inserting tokenized characters within the BASIC line workspace, a check is made to see whether the insertion location has +; reached the address of the error marker. If it has then the parsing of the BASIC line terminates and the count of the typed line characters indicates the equivalent +; position within it of the error. However, should the last character have been a token then the typed line count will also include the number of characters that form +; the keyword, and so this must be subtracted from the count. +; Entry: A=Character to insert. +; DE=Address of insertion position within the BASIC line workspace. +; Exit : If searching for the error marker position and it is found then a return is made directly to the top level calling routine with BC holding the number of characters in +; the typed BASIC line prior to the equivalent error marker position. + +L3EFB: LD HL,(#FD87) ; + INC HL ; Increment the count of the number of characters in the tokenized BASIC line. + LD (#FD87),HL ; + + LD HL,(#FD82) ; HL=Address of next insertion position in the BASIC line workspace. + LD B,A ; Save the character to insert. + + LD A,(#FD8A) ; Fetch the 'locate error marker' flag. + CP #00 ; Searching for the error marker following a syntax error? [Could have saved 1 byte by using AND A] + LD A,B ; A=Character to insert. + JR Z,L3F33 ; Jump if tokenizing the BASIC line. + +;Locating the error marker + + LD DE,(#5C5F) ; X_PTR. Fetch the address of the character after the error marker. + LD A,H ; + CP D ; Has the error marker position possibly been reached? + JR NZ,L3F30 ; Jump ahead if not. + + LD A,L ; + CP E ; Has the error marker position been reached? + JR NZ,L3F30 ; Jump ahead if not. + +;The error marker has been reached + +; [*BUG* - The desired character count until the error marker is held at address #FD85 and needs the length of the last character to be removed from it, +; which for a token would be several bytes. However, the routine simply returns the lower of the tokenized and typed counts, and this yields +; very unhelpful error marker positions shown within the typed BASIC line. Credit: Ian Collier (+3), Andrew Owen (128)] + +; [The code below up until the instruction at #3F2A (ROM 0) should have been as follows. Changes to the code at #3DCD (ROM 0) are also required. Credit: Paul Farrow. +; +; LD HL,(#FD7D) ; Fetch the next address within the Keyword Conversion Buffer. +; LD DE,#FD74 ; Fetch the start address of the Keyword Conversion Buffer. +; AND A ; +; SBC HL,DE ; HL=Length of the keyword (excluding leading or trailing spaces). +; EX DE,HL ; DE=Length of the keyword (excluding leading or trailing spaces). +; LD HL,(#FD85) ; BC=Count of the number of characters in the typed BASIC line until the error marker location was found. +; SBC HL,DE ; Subtract the number of characters in the keyword text. +; LD B,H ; +; LD C,L ; Transfer the result to BC, and then return via the instructions at #3F2A (ROM 0) onwards.] + + LD BC,(#FD85) ; Count of the number of characters in the typed BASIC line until the error marker location was found. + LD HL,(#FD87) ; Count of the number of characters in the tokenized BASIC line until the error marker location. + AND A ; + SBC HL,BC ; + JR NC,L3F2A ; Jump if the tokenized version is longer than the typed version. + + LD BC,(#FD87) ; Count of the number of characters in the tokenized version of the BASIC line until the error marker location. + +L3F2A: LD HL,(#FD8B) ; Fetch the saved stack pointer. + LD SP,HL ; Restore the stack pointer. + SCF ; Set the carry flag to indicate the error marker has been located. + RET ; Return back to the top level calling routine, to #2D04 (ROM 0). + +;The error marker has not yet been reached + +L3F30: SCF ; Set the carry flag to indicate error marker locating mode. + JR L3F35 ; Jump ahead to continue. + +;Tokenizing the BASIC line + +L3F33: SCF ; + CCF ; Reset carry flag to signal BASIC line tokenizing mode. + +L3F35: CALL L1F20 ; Use Normal RAM Configuration (physical RAM bank 0). + + JR NC,L3F47 ; Jump if tokenizing the BASIC line. + +;Searching for the error marker so need to consider embedded floating point numbers + +; [*BUG* - This should fetch the next character from the tokenized BASIC line and not the current character. This routine +; is called to process every visible character in the BASIC line, but is not called for embedded floating point numbers. +; It must therefore test whether the current character is followed by an embedded floating point number and if so to skip +; over it. The routine does make an attempt to detect embedded floating point numbers but incorrectly performs the test +; on the visible character and not the character that follows it. The bug can be fixed as replacing the LD A,(HL) instruction +; with the following instructions. Credit: Paul Farrow. +; +; INC HL ; Advance to the next character in the tokenized BASIC line. +; LD A,(HL) ; Fetch the next character in the tokenized BASIC line. +; DEC HL ; Point back to the current character in the tokenized BASIC line.] + + LD A,(HL) ; Fetch the current character in the tokenized BASIC line. + EX DE,HL ; DE=Insert position within the tokenized BASIC line. + CP #0E ; Is it the 'number' marker? + JR NZ,L3F5D ; Jump ahead if not. + + INC DE ; Skip over the 5 byte hidden number representation. + INC DE ; [*BUG* - There should be another INC DE instruction here to take into account the character that the tokenizer would + INC DE ; have inserted. As a result, the attempt to locate the error marker location will drift off by one byte for every numeric + INC DE ; literal within the BASIC statement, and if there are many numeric literals in the statement then the error marker location + INC DE ; may never be found before the end of the statement is parsed. Credit: Ian Collier (+3), Andrew Owen (128)] + JR L3F5D ; Jump ahead to continue. + +;Come here if tokenizing the BASIC line + +L3F47: PUSH AF ; Save the character to insert and the carry flag reset. + + LD BC,#0001 ; Request to insert 1 byte. + PUSH HL ; + PUSH DE ; + CALL L3F66 ; Check that there is memory available for 1 byte, + POP DE ; automatically producing error '4' if not. + POP HL ; + RST #28 ; BC=Number of bytes. HL=Address location before the position. + DEFW POINTERS ; #1664. Update all system variables due to the insertion. Exit with DE pointing to old STKEND position, BC with number of bytes 'shifted'. + LD HL,(#5C65) ; STKEND. Fetch the start of the spare memory. + EX DE,HL ; DE=Address of spare memory. HL=Address of character in the BASIC line. + LDDR ; Shift up all affected bytes to make the room for the new character. + + POP AF ; Retrieve the character to insert and the flags. The carry flag will be reset and hence will indicate that tokenizing the BASIC line is not complete. + LD (DE),A ; Store the character in the BASIC line workspace. + +L3F5D: INC DE ; Advance to the next character in the BASIC line. + + CALL L1F45 ; Use Workspace RAM configuration (physical RAM bank 7). + + LD (#FD82),DE ; Store the address of the next insertion position within the BASIC line workspace. + RET ; + +; ------------------ +; Room for BC Bytes? +; ------------------ +; Test whether there is room for the specified number of bytes in the spare memory, +; producing error "4 Out of memory" if not. +; Entry: BC=Number of bytes required. +; Exit : Returns if the room requested room is available else an error '4' is produced. + +L3F66: LD HL,(#5C65) ; STKEND. + ADD HL,BC ; Would adding the specified number of bytes overflow the RAM area? + JR C,L3F76 ; Jump to produce an error if so. + + EX DE,HL ; DE=New end address. + LD HL,#0082 ; Would there be at least 130 bytes at the top of RAM? + ADD HL,DE ; + JR C,L3F76 ; Jump to produce an error if not. + + SBC HL,SP ; If the stack is lower in memory, would there still be enough room? + RET C ; Return if there would. + +L3F76: LD A,#03 ; + LD (#5C3A),A ; ERR_NR. Signal error "4 Out of Memory". + JP L0321 ; Jump to error handler routine. + +; ---------------- +; Identify Keyword +; ---------------- +; This routine identifies the string within the Keyword Conversion Buffer and returns +; the token character code. The last character of the string has bit 7 set. +; The routine attempts to identify 48K mode keywords, 128K mode keywords and a number of +; mis-spelled keywords (those that require a space within them). +; Exit: Carry flag set if a keyword was identified. +; A=Token character code. + +L3F7E: CALL #FD2E ; Attempt to identify 48K mode keyword. + RET C ; Return if keyword identified. + +;Attempt to identify 128K mode keywords and mis-spelled keywords. + + LD B,#F9 ; Base character code (results in codes #F9-#FF). + LD DE,#FD74 ; DE=Address of Keyword Conversion Buffer. + LD HL,L3594 ; HL=Keywords string table. + CALL #FD3B ; Attempt to identify 128K mode/mis-spelled keyword. + RET NC ; Return if no keyword identified. + +;Attempt to convert mis-spelled keywords + + CP #FF ; Was it "CLOSE#"? + JR NZ,L3F96 ; + + LD A,#D4 ; Use character code for 'CLOSE #'. + JR L3FB8 ; Jump ahead to continue. + +L3F96: CP #FE ; Was it "OPEN#"? + JR NZ,L3F9E ; Jump if not. + + LD A,#D3 ; Use character code for 'OPEN #'. + JR L3FB8 ; Jump ahead to continue. + +L3F9E: CP #FD ; Was it "DEFFN"? + JR NZ,L3FA6 ; Jump if not. + + LD A,#CE ; Use character code for 'DEF FN'. + JR L3FB8 ; Jump ahead to continue. + +L3FA6: CP #FC ; Was it "GOSUB"? + JR NZ,L3FAE ; Jump if not. + + LD A,#ED ; Use character code for 'GO SUB'. + JR L3FB8 ; Jump ahead to continue. + +L3FAE: CP #FB ; Was it "GOTO"? + JR NZ,L3FB6 ; Jump if not. + + LD A,#EC ; Use character code for 'GO TO'. + JR L3FB8 ; Jump ahead to continue. + +L3FB6: SUB #56 ; Reduce to #A3 for 'SPECTRUM' and #A4 for 'PLAY'. + +L3FB8: SCF ; Signal keyword identified. + RET ; + +; --------------- +; Copy Data Block +; --------------- +; This routine is used on 8 occasions to copy a block of default data. +; Entry: DE=Destination address. +; HL=Address of source data table, which starts with the number of bytes to copy +; followed by the bytes themselves. + +L3FBA: LD B,(HL) ; Get number of bytes to copy. + INC HL ; Point to the first byte to copy. + +L3FBC: LD A,(HL) ; Fetch the byte from the source + LD (DE),A ; and copy it to the destination. + INC DE ; Increment destination address. + INC HL ; Increment source address. + DJNZ L3FBC ; Repeat for all bytes. + + RET ; + +; ------------------------------------- +; Get Numeric Value for ASCII Character +; ------------------------------------- +; Exit: Carry flag set if character was numeric and A holding value. +; +; [Never called by this ROM] + +L3FC3: CP '0' ; #30. Test against '0'. + CCF ; + RET NC ; Return with carry flag reset if not numeric character. + + CP ':' ; #3A. Test against ':'. + RET NC ; Return with carry flag reset if not numeric character. + + SUB '0' ; #30. Get numeric value. + SCF ; Return with carry flag set to indicate a numeric character. + RET ; + +; --------------------------- +; Call Action Handler Routine +; --------------------------- +; If the code in A matches an entry in the table pointed to by HL +; then execute the action specified by the entry's routine address. +; Entry: A=Code. +; HL=Address of action table. +; Exit : Zero flag reset if no match found. +; Carry flag reset if an error beep is required, or to signal no suitable action handler found. +; HL=Address of next table entry if a match was found. + +L3FCE: PUSH BC ; Save registers. + PUSH DE ; + + LD B,(HL) ; Fetch number of table entries. + INC HL ; Point to first entry. + +L3FD2: CP (HL) ; Possible match for A? + INC HL ; + LD E,(HL) ; + INC HL ; + LD D,(HL) ; DE=Address to call if a match. + JR Z,L3FE1 ; Jump if a match. + + INC HL ; Next table entry. + DJNZ L3FD2 ; Repeat for next table entry. + +;No match found + + SCF ; Return with carry flag reset to signal an error beep is required + CCF ; and with the zero flag reset to signal a match was not found. + + POP DE ; Restore registers. + POP BC ; + RET ; + +;Found a match + +L3FE1: EX DE,HL ; HL=Action routine to call. + + POP DE ; + POP BC ; + + CALL L3FEE ; Indirectly call the action handler routine. + JR C,L3FEB ; Jump if no error beep is required. + + CP A ; Set zero flag to indicate a match was found. + RET ; Exit with carry flag reset to indicate error beep required. + +L3FEB: CP A ; Set zero flag to indicate a match was found. + SCF ; Signal no error beep required. + RET ; + +L3FEE: JP (HL) ; Jump to the action handler routine. + + +; ===================== +; PROGRAMMERS' INITIALS +; ===================== +; [Provided by Andrew Owen] + +L3FEF: DEFB #00 + DEFM "MB" ; Martin Brennan. + DEFB #00 + DEFM "SB" ; Steve Berry. + DEFB #00 + DEFM "AC" ; Andrew Cummins. + DEFB #00 + DEFM "RG" ; Rupert Goodwins. + DEFB #00 + DEFM "KM" ; Kevin Males. + DEFB #00 + + +; ================= +; END OF ROM MARKER +; ================= + +L3FFF: DEFB #01 ; + +; + BLOCK #4000-$,#FF +; +; ============================== +; REFERENCE INFORMATION - PART 2 +; ============================== + +; ================================== +; Routines Copied/Constructed in RAM +; ================================== + +; -------------------------------- +; Construct Keyword Representation +; -------------------------------- +; This routine copies a keyword string from ROM 1 into the BASIC Line Construction Buffer, +; terminating it with an 'end of BASIC line' marker (code ' '+#80). Only standard Spectrum +; keywords are handled by this routine (SPECTRUM and PLAY are processed elsewhere). +; The routine is run from RAM bank 7 at #FCAE so that access to both ROMs is available. +; Depending on the value of A (which should be the ASCII code less #A5, +; e.g. 'RND', the first (48K) keyword, has A=0), a different index into the +; token table is taken. This is to allow speedier lookup since there are never more +; than 15 keywords to advance through. +; Entry: A=Keyword character code-#A5 (range #00-#5A). +; DE=Insertion address within BASIC Line Construction Buffer. +; +; Copied to physical RAM bank 7 at #FCAE-#FCFC by routine at #335F (ROM 0). +; +;LFCAE DI ; Disable interrupts whilst paging. +; +; LD BC,#7FFD ; +; LD D,#17 ; Page in ROM 1, SCREEN 0, no locking, RAM bank 7. +; OUT (C),D ; +; +; CP #50 ; Was the token #F5 or above? +; JR NC,#FCEB ; +; +; CP #40 ; Was the token #E5 or above? +; JR NC,#FCE4 ; +; +; CP #30 ; Was the token #D5 or above? +; JR NC,#FCDD ; +; +; CP #20 ; Was the token #C5 or above? +; JR NC,#FCD6 ; +; +; CP #10 ; Was the token #B5 or above? +; JR NC,#FCCF ; +; +;Used for token range #A5-#B4 (#00 <= A <= #0F) +; +; LD HL,#0096 ; Token table entry 'RND' in ROM 1. +; JR #FCF0 ; +; +;Used for token range #B5-#C4 (#10 <= A <= #1F) +; +;LFCCF SUB #10 ; +; LD HL,#00CF ; Token table entry 'ASN' in ROM 1. +; JR #FCF0 ; +; +;Used for token range #C5-#D4 (#20 <= A <= #2F) +; +;LFCD6 SUB #20 ; +; LD HL,#0100 ; Token table entry 'OR' in ROM 1. +; JR #FCF0 ; +; +;Used for token range #D5-#E4 (#30 <= A <= #3F) +; +;LFCDD SUB #30 ; +; LD HL,#013E ; Token table entry 'MERGE' in ROM 1. +; JR #FCF0 ; +; +;Used for token range #E5-#F4 (#40 <= A <= #4F) +; +;LFCE4 SUB #40 ; +; LD HL,#018B ; Token table entry 'RESTORE' in ROM 1. +; JR #FCF0 ; +; +;Used for token range #F5-#FF (A >= #50) +; +;LFCEB SUB #50 ; +; LD HL,#01D4 ; Token table entry 'PRINT' in ROM 1. +; +;LFCF0 LD B,A ; Take a copy of the index value. +; OR A ; If A=0 then already have the entry address. +; +;LFCF2 JR Z,#FCFD ; If indexed item found then jump ahead to copy the characters of the token. +; +;LFCF4 LD A,(HL) ; Fetch a character. +; INC HL ; Point to next character. +; AND #80 ; Has end of token marker been found? +; JR Z,#FCF4 ; Loop back for next character if not. +; +; DEC B ; Count down the index of the required token. +;LFCFC JR #FCF2 ; Jump back to test whether the required token has been reached. + +; ----------------------- +; Copy Keyword Characters +; ----------------------- +; This routine copies a keyword string from ROM 1 into the BASIC Line Construction Buffer, +; terminating it with an 'end of BASIC line' marker (code ' '+#80). +; The routine is run from RAM bank 7 so that access to both ROMs is available. +; Entry: HL=Address of keyword string in ROM 1. +; DE=Insertion address within BASIC Line Construction Buffer. +; +; Copied to physical RAM bank 7 at #FCFD-#FD2D by subroutine at #335F (ROM 0). +; +;LFCFD LD DE,#FCA3 ; DE=Keyword Construction Buffer. +; LD (#FCA1),DE ; Store the start address of the constructed keyword. +; +; LD A,(#FC9E) ; Print a leading space? +; OR A ; +; LD A,#00 ; +; LD (#FC9E),A ; Signal leading space not required. +; JR NZ,#FD13 ; Jump if leading space not required. +; +; LD A,#20 ; Print a leading space. +; LD (DE),A ; Insert a leading space. +; INC DE ; Advance to next buffer position. +; +;LFD13 LD A,(HL) ; Fetch a character of the keyword. +; LD B,A ; Store it. +; INC HL ; Advance to next keyword character. +; LD (DE),A ; Store the keyword character in the BASIC line buffer. +; INC DE ; Advance to the next buffer position. +; AND #80 ; Test if the end of the keyword string. +; JR Z,#FD13 ; Jump back if not to repeat for all characters of the keyword. +; +; LD A,B ; Get keyword character back. +; AND #7F ; Mask of bit 7 which indicates the end of string marker. +; DEC DE ; Point back at the last character of the keyword copied into the buffer +; LD (DE),A ; and store it. +; +; INC DE ; Advance to the position in the buffer after the last character of the keyword. +; LD A,' '+#80 ; #A0. ' ' + end marker +; LD (DE),A ; Store an 'end of BASIC line so far' marker. +; +; LD A,#07 ; +; LD BC,#7FFD ; +; OUT (C),A ; Page in ROM 0, SCREEN 0, no locking, RAM bank 7. +; EI ; Re-enable interrupts. +;LFD2D RET ; + +; -------------- +; Identify Token +; -------------- +; This routine identifies the string within the Keyword Conversion Buffer and returns +; the character code. The last character of the string to identify has bit 7 set. +; Exit: Carry flag set if token identified. +; B=Character code. +; +; Copied to physical RAM bank 7 at #FD2E-#FD69 by subroutine at #335F (ROM 0). +; +;LFD2E DI ; Disable interrupts whilst paging. +; LD BC,#7FFD ; +; LD D,#17 ; Select ROM 1, SCREEN 0, RAM bank 7. +; OUT (C),D ; +; +; LD HL,#0096 ; Address of token table in ROM 1. +; LD B,#A5 ; Character code of the first token - 'RND'. +; +;Entry point here used to match 128K mode tokens and mis-spelled tokens +; +;LFD3B LD DE,#FD74 ; Keyword Conversion Buffer holds the text to match against. +; +;LFD3E LD A,(DE) ; Fetch a character from the buffer. +; AND #7F ; Mask off terminator bit. +; CP #61 ; Is it lowercase? +; LD A,(DE) ; Fetch the character again from the buffer. +; JR C,#FD48 ; Jump if uppercase. +; +; AND #DF ; Make the character uppercase. +; +;LFD48 CP (HL) ; Does the character match the current item in the token table? +; JR NZ,#FD54 ; Jump if it does not. +; +; INC HL ; Point to the next character in the buffer. +; INC DE ; Point to the next character in the token table. +; AND #80 ; Has the terminator been reached? +; JR Z,#FD3E ; Jump back if not to test the next character in the token. +; +;A match was found +; +; SCF ; Signal a match was found. +; JR #FD60 ; Jump ahead to continue. +; +;LFD54 INC B ; The next character code to test against. +; JR Z,#FD5F ; Jump if all character codes tested. +; +;The token does not match so skip to the next entry in the token table +; +;LFD57 LD A,(HL) ; Fetch the character from the token table. +; AND #80 ; Has the end terminator been found? +; INC HL ; Point to the next character. +; JR Z,#FD57 ; Jump back if no terminator found. +; +; JR #FD3B ; Jump back to test against the next token. +; +;All character codes tested and no match found +; +;LFD5F OR A ; Clear the carry flag to indicate no match found. +; +;The common exit point +; +;LFD60 LD A,B ; Fetch the character code of the matching token (#00 for no match). +; +; LD D,#07 ; Select ROM 0, SCREEN 0, RAM bank 7. +; LD BC,#7FFD ; +; OUT (C),D ; +; EI ; Re-enable interrupts. +;LFD69 RET ; + +; ---------------------------------- +; Insert Character into Display File +; ---------------------------------- +; Copy a character into the display file. +; Entry: HL=Character data. +; DE=Display file address. +; This routine is constructed from three segments and stitched together in physical RAM bank 7 to form a single routine. +; +; Created in physical RAM Bank 7 at #FF28-#FF60 by routine at #246F (ROM 0). [Construction routine never actually called by the ROM] +; +;LFF28 PUSH BC ; Save BC +; +; DI ; Disable interrupts whilst paging. +; +; LD BC,#7FFD ; +; LD A,(BANK_M) ; #5B5C. Fetch current paging configuration. +; XOR #10 ; Toggle ROMs. +; OUT (C),A ; Perform paging. +; EI ; Re-enable interrupts. +; EX AF,AF' ; Save the new configuration in A'. +; +; LD C,D ; Save D. +; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 1. +; +; INC HL ; +; INC D ; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 2. +; +; INC HL ; +; INC D ; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 3. +; +; INC HL ; +; INC D ; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 4. +; +; INC HL ; +; INC D ; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 5. +; +; INC HL ; +; INC D ; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 6. +; +; INC HL ; +; INC D ; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 7. +; +; INC HL ; +; INC D ; +; LD A,(HL) ; +; LD (DE),A ; Copy byte 8. +; +; LD D,C ; Restore D. +; +; EX AF,AF' ; Retrieve current paging configuration. +; DI ; Disable interrupts whilst paging. +; LD C,#FD ; Restore Paging I/O port number. +; XOR #10 ; Toggle ROMs. +; OUT (C),A ; Perform paging. +; EI ; Re-enable interrupts. +; +; POP BC ; Restore BC. +;LFF60 RET ; + + +; =========================== +; Standard Error Report Codes +; =========================== + +; 0 - OK Successful completion, or jump to a line number bigger than any existing. +; 1 - NEXT without FOR The control variable does not exist (it has not been set up by a FOR statement), +; but there is an ordinary variable with the same name. +; 2 - Variable not found For a simple variable, this will happen if the variable is used before it has been assigned +; to by a LET, READ or INPUT statement, loaded from disk (or tape), or set up in a FOR statement. +; For a subscripted variable, it will happen if the variable is used before it has been +; dimensioned in a DIM statement, or loaded from disk (or tape). +; 3 - Subscript wrong A subscript is beyond the dimension of the array or there are the wrong number of subscripts. +; 4 - Out of memory There is not enough room in the computer for what you are trying to do. +; 5 - Out of screen An INPUT statement has tried to generate more than 23 lines in the lower half of the screen. +; Also occurs with 'PRINT AT 22,xx'. +; 6 - Number too big Calculations have yielded a number greater than approximately 10^38. +; 7 - RETURN without GO SUB There has been one more RETURN than there were GO SUBs. +; 8 - End of file Input returned unacceptable character code. +; 9 - STOP statement After this, CONTINUE will not repeat the STOP but carries on with the statement after. +; A - Invalid argument The argument for a function is unsuitable. +; B - Integer out of range When an integer is required, the floating point argument is rounded to the nearest integer. +; If this is outside a suitable range, then this error results. +; C - Nonsense in BASIC The text of the (string) argument does not form a valid expression. +; D - BREAK - CONT repeats BREAK was pressed during some peripheral operation. +; E - Out of DATA You have tried to READ past the end of the DATA list. +; F - Invalid file name SAVE with filename empty or longer than 10 characters. +; G - No room for line There is not enough room left in memory to accommodate the new program line. +; H - STOP in INPUT Some INPUT data started with STOP. +; I - FOR without NEXT A FOR loop was to be executed no times (e.g. FOR n=1 TO 0) and corresponding NEXT statement could not be found. +; J - Invalid I/O device Attempting to input characters from or output characters to a device that doesn't support it. +; K - Invalid colour The number specified is not an appropriate value. +; L - BREAK into program BREAK pressed. This is detected between two statements. +; M - RAMTOP no good The number specified for RAMTOP is either too big or too small. +; N - Statement lost Jump to a statement that no longer exists. +; O - Invalid Stream Trying to input from or output to a stream that isn't open or that is out of range (0...15), +; or trying to open a stream that is out of range. +; P - FN without DEF User-defined function used without a corresponding DEF in the program. +; Q - Parameter error Wrong number of arguments, or one of them is the wrong type. +; R - Tape loading error A file on tape was found but for some reason could not be read in, or would not verify. + + +; ========================= +; Standard System Variables +; ========================= +; These occupy addresses #5C00-#5CB5. +; +; KSTATE EQU #5C00 ; 8 IY-#3A Used in reading the keyboard. +; LASTK EQU #5C08 ; 1 IY-#32 Stores newly pressed key. +; REPDEL EQU #5C09 ; 1 IY-#31 Time (in 50ths of a second) that a key must be held down before it repeats. This starts off at 35. +; REPPER EQU #5C0A ; 1 IY-#30 Delay (in 50ths of a second) between successive repeats of a key held down - initially 5. +; DEFADD EQU #5C0B ; 2 IY-#2F Address of arguments of user defined function (if one is being evaluated), otherwise 0. +; K_DATA EQU #5C0D ; 1 IY-#2D Stores second byte of colour controls entered from keyboard. +; TVDATA EQU #5C0E ; 2 IY-#2C Stores bytes of colour, AT and TAB controls going to TV. +; STRMS EQU #5C10 ; 38 IY-#2A Addresses of channels attached to streams. +; CHARS EQU #5C36 ; 2 IY-#04 256 less than address of character set, which starts with ' ' and carries on to '(c)'. +; RASP EQU #5C38 ; 1 IY-#02 Length of warning buzz. +; PIP EQU #5C39 ; 1 IY-#01 Length of keyboard click. +; ERR_NR EQU #5C3A ; 1 IY+#00 1 less than the report code. Starts off at 255 (for -1) so 'PEEK 23610' gives 255. +; FLAGS EQU #5C3B ; 1 IY+#01 Various flags to control the BASIC system: +; Bit 0: 1=Suppress leading space. +; Bit 1: 1=Using printer, 0=Using screen. +; Bit 2: 1=Print in L-Mode, 0=Print in K-Mode. +; Bit 3: 1=L-Mode, 0=K-Mode. +; Bit 4: 1=128K Mode, 0=48K Mode. [Always 0 on 48K Spectrum] +; Bit 5: 1=New key press code available in LAST_K. +; Bit 6: 1=Numeric variable, 0=String variable. +; Bit 7: 1=Line execution, 0=Syntax checking. +TV_FLAGS EQU TVFLAG ;!FIXIT старый вариант названия +TVFLAG EQU #5C3C ; 1 IY+#02 Flags associated with the TV: +; Bit 0 : 1=Using lower editing area, 0=Using main screen. +; Bit 1-2: Not used (always 0). +; Bit 3 : 1=Mode might have changed. +; Bit 4 : 1=Automatic listing in main screen, 0=Ordinary listing in main screen. +; Bit 5 : 1=Lower screen requires clearing after a key press. +; Bit 6 : 1=Tape Loader option selected (set but never tested). [Always 0 on 48K Spectrum] +; Bit 7 : Not used (always 0). +; ERR_SP EQU #5C3D ; 2 IY+#03 Address of item on machine stack to be used as error return. +; LISTSP EQU #5C3F ; 2 IY+#05 Address of return address from automatic listing. +; MODE EQU #5C41 ; 1 IY+#07 Specifies cursor type: +; #00='L' or 'C'. +; #01='E'. +; #02='G'. +; #04='K'. +; NEWPPC EQU #5C42 ; 2 IY+#08 Line to be jumped to. +; NSPPC EQU #5C44 ; 1 IY+#0A Statement number in line to be jumped to. +; PPC EQU #5C45 ; 2 IY+#0B Line number of statement currently being executed. +; SUBPPC EQU #5C47 ; 1 IY+#0D Number within line of statement currently being executed. +; BORDCR EQU #5C48 ; 1 IY+#0E Border colour multiplied by 8; also contains the attributes normally used for the lower half +; of the screen. +; E_PPC EQU #5C49 ; 2 IY+#0F Number of current line (with program cursor). +; VARS EQU #5C4B ; 2 IY+#11 Address of variables. +; DEST EQU #5C4D ; 2 IY+#13 Address of variable in assignment. +; CHANS EQU #5C4F ; 2 IY+#15 Address of channel data. +; CURCHL EQU #5C51 ; 2 IY+#17 Address of information currently being used for input and output. +; PROG EQU #5C53 ; 2 IY+#19 Address of BASIC program. +; NXTLIN EQU #5C55 ; 2 IY+#1B Address of next line in program. +; DATADD EQU #5C57 ; 2 IY+#1D Address of terminator of last DATA item. +E_LINE EQU #5C59 ; 2 IY+#1F Address of command being typed in. +; K_CUR EQU #5C5B ; 2 IY+#21 Address of cursor. +; CH_ADD EQU #5C5D ; 2 IY+#23 Address of the next character to be interpreted - the character after the argument of PEEK, +; or the NEWLINE at the end of a POKE statement. +; X_PTR EQU #5C5F ; 2 IY+#25 Address of the character after the '?' marker. +; WORKSP EQU #5C61 ; 2 IY+#27 Address of temporary work space. +; STKBOT EQU #5C63 ; 2 IY+#29 Address of bottom of calculator stack. +; STKEND EQU #5C65 ; 2 IY+#2B Address of start of spare space. +; BREG EQU #5C67 ; 1 IY+#2D Calculator's B register. +; MEM EQU #5C68 ; 2 IY+#2E Address of area used for calculator's memory (usually MEMBOT, but not always). +; FLAGS2 EQU #5C6A ; 1 IY+#30 Flags: +; Bit 0 : 1=Screen requires clearing. +; Bit 1 : 1=Printer buffer contains data. +; Bit 2 : 1=In quotes. +; Bit 3 : 1=CAPS LOCK on. +; Bit 4 : 1=Using channel 'K'. +; Bit 5-7: Not used (always 0). +; DF_SZ EQU #5C6B ; 1 IY+#31 The number of lines (including one blank line) in the lower part of the screen. +; S_TOP EQU #5C6C ; 2 IY+#32 The number of the top program line in automatic listings. +; OLDPPC EQU #5C6E ; 2 IY+#34 Line number to which CONTINUE jumps. +; OSPPC EQU #5C70 ; 1 IY+#36 Number within line of statement to which CONTINUE jumps. +; FLAGX EQU #5C71 ; 1 IY+#37 Flags: +; Bit 0 : 1=Simple string complete so delete old copy. +; Bit 1 : 1=Indicates new variable, 0=Variable exists. +; Bit 2-4: Not used (always 0). +; Bit 5 : 1=INPUT mode. +; Bit 6 : 1=Numeric variable, 0=String variable. Holds nature of existing variable. +; Bit 7 : 1=Using INPUT LINE. +; STRLEN EQU #5C72 ; 2 IY+#38 Length of string type destination in assignment. +; T_ADDR EQU #5C74 ; 2 IY+#3A Address of next item in syntax table. +; SEED EQU #5C76 ; 2 IY+#3C The seed for RND. Set by RANDOMIZE. +; FRAMES EQU #5C78 ; 3 IY+#3E 3 byte (least significant byte first), frame counter incremented every 20ms. +; UDG EQU #5C7B ; 2 IY+#41 Address of first user-defined graphic. Can be changed to save space by having fewer +; user-defined characters. +; COORDS EQU #5C7D ; 1 IY+#43 X-coordinate of last point plotted. +; EQU #5C7E ; 1 IY+#44 Y-coordinate of last point plotted. +; P_POSN EQU #5C7F ; 1 IY+#45 33-column number of printer position. +; PR_CC EQU #5C80 ; 2 IY+#46 Full address of next position for LPRINT to print at (in ZX Printer buffer). +; Legal values #5B00 - #5B1F. [Not used in 128K mode] +; ECHO_E EQU #5C82 ; 2 IY+#48 33-column number and 24-line number (in lower half) of end of input buffer. +; DF_CC EQU #5C84 ; 2 IY+#4A Address in display file of PRINT position. +; DF_CCL EQU #5C86 ; 2 IY+#4C Like DF CC for lower part of screen. +; S_POSN EQU #5C88 ; 1 IY+#4E 33-column number for PRINT position. +; EQU #5C89 ; 1 IY+#4F 24-line number for PRINT position. +; SPOSNL EQU #5C8A ; 2 IY+#50 Like S_POSN for lower part. +; SCR_CT EQU #5C8C ; 1 IY+#52 Counts scrolls - it is always 1 more than the number of scrolls that will be done before +; stopping with 'scroll?'. +; ATTR_P EQU #5C8D ; 1 IY+#53 Permanent current colours, etc, as set up by colour statements. +; MASK_P EQU #5C8E ; 1 IY+#54 Used for transparent colours, etc. Any bit that is 1 shows that the corresponding attribute +; bit is taken not from ATTR_P, but from what is already on the screen. +; ATTR_T EQU #5C8F ; 1 IY+#55 Temporary current colours (as set up by colour items). +; MASK_T EQU #5C90 ; 1 IY+#56 Like MASK_P, but temporary. +; P_FLAG EQU #5C91 ; 1 IY+#57 Flags: +; Bit 0: 1=OVER 1, 0=OVER 0. +; Bit 1: Not used (always 0). +; Bit 2: 1=INVERSE 1, 0=INVERSE 0. +; Bit 3: Not used (always 0). +; Bit 4: 1=Using INK 9. +; Bit 5: Not used (always 0). +; Bit 6: 1=Using PAPER 9. +; Bit 7: Not used (always 0). +; MEMBOT EQU #5C92 ; 30 IY+#58 Calculator's memory area - used to store numbers that cannot conveniently be put on the +; calculator stack. +; EQU #5CB0 ; 2 IY+#76 Not used on standard Spectrum. [Used by ZX Interface 1 Edition 2 for printer WIDTH] +; RAMTOP EQU #5CB2 ; 2 IY+#78 Address of last byte of BASIC system area. +; P_RAMT EQU #5CB4 ; 2 IY+#7A Address of last byte of physical RAM. + + +; ========== +; Memory Map +; ========== +; The conventional memory is used as follows: +; +; +---------+-----------+------------+--------------+-------------+-- +; | BASIC | Display | Attributes | New System | System | +; | ROM | File | File | Variables | Variables | +; +---------+-----------+------------+--------------+-------------+-- +; ^ ^ ^ ^ ^ ^ +; #0000 #4000 #5800 #5B00 #5C00 #5CB6 = CHANS +; +; +; --+----------+---+---------+-----------+---+------------+--+---+-- +; | Channel |#80| BASIC | Variables |#80| Edit Line |NL|#80| +; | Info | | Program | Area | | or Command | | | +; --+----------+---+---------+-----------+---+------------+--+---+-- +; ^ ^ ^ ^ ^ +; CHANS PROG VARS E_LINE WORKSP +; +; +; ------> <------- <------ +; --+-------+--+------------+-------+-------+---------+-------+-+---+------+ +; | INPUT |NL| Temporary | Calc. | Spare | Machine | GOSUB |?|#3E| UDGs | +; | data | | Work Space | Stack | | Stack | Stack | | | | +; --+-------+--+------------+-------+-------+---------+-------+-+---+------+ +; ^ ^ ^ ^ ^ ^ ^ +; WORKSP STKBOT STKEND SP RAMTOP UDG P_RAMT + + +; ========== +; I Register +; ========== +; The I register is used along with the R register by the Z80 for automatic memory refreshing. Setting the I register to a value between #40 and #7F +; causes memory refreshes to occur to the lower 16K RAM. This RAM is contended with the ULA which uses it for the generation of the video display. +; The memory refreshes get interpreted by the ULA as the CPU requesting to access the lower 16K RAM bank very rapidly and very often. The ULA is not +; able to handle reads at such a high frequency, with the consequence that it fails to fetch and output the next screen byte. Instead it uses re-uses +; the byte previously read. This causes a visible corruption to the video display output, often referred to a 'snow', although no actual corruption +; occurs to the video display RAM. This also happens when the I register is set to a value between #C0 and #FF when a contended RAM bank is paged in +; and, unlike the Spectrum 16K/48K, can lead to a machine crash. + + +; =================== +; Screen File Formats +; =================== +; The two screens available on the Spectrum 128, the normal screen in RAM bank 5 (#4000-#5AFF) and the shadow screen in +; RAM bank 7 (#C000-#FFFF), both use the same file format. +; +; ------------ +; Display File +; ------------ +; The display file consists of 3 areas, each consisting of 8 characters rows, with each row consisting of 8 pixel lines. +; Each pixel line consists of 32 cell columns, with each cell consisting of a byte that represents 8 pixels. +; +; The address of a particular cell is formed as follows: +; +; +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +; | s | 1 | 0 | a | a | l | l | l | | r | r | r | c | c | c | c | c | +; +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +; Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +; +; where: s = Screen (0-1: 0=Normal screen, 1=Shadow Screen) +; aa = Area (0-2) +; rrr = Row (0-7) +; lll = Line (0-7) +; ccccc = Column (0-31) +; +; An area value of 3 denotes the attributes file, which consists of a different format. +; +; --------------- +; Attributes File +; --------------- +; The attributes file consists of 24 characters rows, with each row consisting of 32 cell columns. +; Each cell consisting of a byte that holds the colour information. +; +; The address of a particular cell is formed as follows: +; +; +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +; | s | 1 | 0 | 1 | 1 | 0 | r | r | | r | r | r | c | c | c | c | c | +; +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+ +; Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +; +; where: s = Screen (0-1: 0=Normal screen, 1=Shadow Screen) +; rrrrr = Row (0-23) +; ccccc = Column (0-31) +; +; +; Each cell holds a byte of colour information: +; +; +---+---+---+---+---+---+---+---+ +; | f | b | p | p | p | i | i | i | +; +---+---+---+---+---+---+---+---+ +; Bit: 7 6 5 4 3 2 1 0 +; +; where: f = Flash (0-1: 0=Off, 1=On) +; b = Bright (0-1: 0=Off, 1=On) +; ppp = Paper (0-7: 0=Black, 1=Blue, 2=Red, 3=Magenta, 4=Green, 5=Cyan, 6=Yellow, 7=White) +; iii = Ink (0-7: 0=Black, 1=Blue, 2=Red, 3=Magenta, 4=Green, 5=Cyan, 6=Yellow, 7=White) +; +; ----------------------------------------------------------- +; Address Conversion Between Display File and Attributes File +; ----------------------------------------------------------- +; The address of the attribute cell corresponding to an address in the display file can be constructed by moving bits 11 to 12 (the area value) +; to bit positions 8 to 9, setting bit 10 to 0 and setting bits 11 to 12 to 1. +; +; The address of the display file character cell corresponding to an address in the attributes file can be constructed by moving bits 8 to 9 (the row value) +; to bit positions 11 to 12, and then setting bits 8 to 9 to 0. + + +; ================== +; Standard I/O Ports +; ================== + +; -------- +; Port #FE +; -------- +; This controls the cassette interface, the speaker, the border colour and is used to read the keyboard. +; Since it is the ULA that controls these facilities, it will introduce a delay when accessing the port if +; it is busy at the time, and hence I/O port #FE is subject to contention. +; +; OUTPUT: +; +; Bit 0-2: Border colour (0=Black, 1=Blue, 2=Red, 3=Magenta, 4=Green, 5=Cyan, 6=Yellow, 7=White). +; Bit 3 : MIC output (1=Off, 0=On). +; Bit 4 : Speaker output (1=On, 0=Off). +; Bit 5-7: Not used. +; +; INPUT: +; +; Upper byte selects keyboard row to read. +; +; Bit0 Bit1 Bit2 Bit3 Bit4 Bit4 Bit3 Bit2 Bit1 Bit0 +; ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- +; #F7FE = 1 2 3 4 5 6 7 8 9 0 = #EFFE +; #FBFE = Q W E R T Y U I O P = #DFFE +; #FDFE = A S D F G H J K L ENTER = #BFFE +; #FEFE = SHIFT Z X C V B N M SYM SPACE = #7FFE +; +; Bit 0-4 : Key states (corresponding bit is 0 if the key is pressed). +; Bit 5 : Not used (always 1). +; Bit 6 : EAR input. +; Bit 7 : Not used (always 1). + + +; ====================== +; Cassette Header Format +; ====================== +; +; A file consists of a header block followed by a data block. Each block begins with a flag that +; indicates whether it is a header block or a data block. Next are the header or data bytes, +; and finally a checksum of the flag and header/data bytes. +; +; Flag - A value of #00 for a header and #FF for a data block. +; Bytes - The bytes forming the header information or the file data. +; Checksum - An XOR checksum of the Flag and Bytes fields. +; +; The header information consists of 17 bytes and these describe the size and type of data that the +; data block contains. +; +; The header bytes have the following meaning: +; Byte #00 : File type - #00=Program, #01=Numeric array, #02=Character array, #03=Code/Screen$. +; Bytes #01-#0A: File name, padding with trailing spaces. +; Bytes #0B-#0C: Length of program/code block/screen$/array (#1B00 for screen$). +; Bytes #0D-#0E: For a program, it holds the auto-run line number (#80 in byte #0E if no auto-run). +; For code block/screen$ it holds the start address (#4000 for screen$). +; For an array, it holds the variable name in byte #0E. +; Bytes #0F-#10: Offset to the variables (i.e. length of program) if a program. + + +; ================================================ +; AY-3-8912 Programmable Sound Generator Registers +; ================================================ +; This is controlled through output I/O port #FFFD. It is driven from a 1.77345 MHz clock. +; The datasheet for the AY-3-8912 lists to the registers in octal, but below they are listed in decimal. +; +; ----------------- +; Registers 0 and 1 (Channel A Tone Generator) +; ----------------- +; Forms a 12 bit pitch control for sound channel A. The basic unit of tone is the clock +; frequency divided by 16, i.e. 110.841 kHz. With a 12 bit counter range, 4095 different +; frequencies from 27.067 Hz to 110.841 kHz (in increments of 27.067 Hz) can be generated. +; +; Bits 0-7 : Contents of register 0. +; Bits 8-11 : Contents of lower nibble of register 1. +; Bits 12-15: Not used. +; +; ----------------- +; Registers 2 and 3 (Channel B Tone Generator) +; ----------------- +; Forms a 12 bit pitch control for sound channel B. +; +; Bits 0-7 : Contents of register 2. +; Bits 8-11 : Contents of lower nibble of register 3. +; Bits 12-15: Not used. +; +; ----------------- +; Registers 4 and 5 (Channel C Tone Generator) +; ----------------- +; Forms a 12 bit pitch control for sound channel C. +; +; Bits 0-7 : Contents of register 4. +; Bits 8-11 : Contents of lower nibble of register 5. +; Bits 12-15: Not used. +; +; ---------- +; Register 6 (Noise Generator) +; ---------- +; The frequency of the noise is obtained in the PSG by first counting down the input +; clock by 16 (i.e. 110.841 kHz), then by further counting down the result by the programmed +; 5 bit noise period value held in bits 0-4 of register 6. With a 5 bit counter range, 31 different +; frequencies from 3.576 kHz to 110.841 kHz (in increments of 3.576 kHz) can be generated. +; +; ---------- +; Register 7 (Mixer - I/O Enable) +; ---------- +; This controls the enable status of the noise and tone mixers for the three channels, +; and also controls the I/O port used to drive the RS232 and Keypad sockets. +; +; Bit 0: Channel A Tone Enable (0=enabled). +; Bit 1: Channel B Tone Enable (0=enabled). +; Bit 2: Channel C Tone Enable (0=enabled). +; Bit 3: Channel A Noise Enable (0=enabled). +; Bit 4: Channel B Noise Enable (0=enabled). +; Bit 5: Channel C Noise Enable (0=enabled). +; Bit 6: I/O Port Enable (0=input, 1=output). +; Bit 7: Not used. +; +; ---------- +; Register 8 (Channel A Volume) +; ---------- +; This controls the volume of channel A. +; +; Bits 0-4: Channel A volume level. +; Bit 5 : 1=Use envelope defined by register 13 and ignore the volume setting. +; Bits 6-7: Not used. +; +; ---------- +; Register 9 (Channel B Volume) +; ---------- +; This controls the volume of channel B. +; +; Bits 0-4: Channel B volume level. +; Bit 5 : 1=Use envelope defined by register 13 and ignore the volume setting. +; Bits 6-7: Not used. +; +; ----------- +; Register 10 (Channel C Volume) +; ----------- +; This controls the volume of channel C. +; +; Bits 0-4: Channel C volume level. +; Bit 5 : 1=Use envelope defined by register 13 and ignore the volume setting. +; Bits 6-7: Not used. +; +; ------------------ +; Register 11 and 12 (Envelope Period) +; ------------------ +; These registers allow the frequency of the envelope to be selected. +; The frequency of the envelope is obtained in the PSG by first counting down +; the input clock by 256 (6.927 kHz), then further counting down the result by the programmed +; 16 bit envelope period value. With a 16 bit counter range, 65535 different +; frequencies from 1.691 Hz to 110.841 kHz (in increments of 1.691 Hz) can be generated. +; +; Bits 0-7 : Contents of register 11. +; Bits 8-15: Contents of register 12. +; +; ----------- +; Register 13 (Envelope Shape) +; ----------- +; This register allows the shape of the envelope to be selected. +; The envelope generator further counts down the envelope frequency by 16, producing +; a 16-state per cycle envelope pattern. The particular shape and cycle pattern of any +; desired envelope is accomplished by controlling the count pattern of the 4 bit counter +; and by defining a single cycle or repeat cycle pattern. +; +; Bit 0 : Hold. +; Bit 1 : Alternate. +; Bit 2 : Attack. +; Bit 3 : Continue. +; Bits 4-7: Not used. +; +; These control bits can produce the following envelope waveforms: +; +; Bit: 3 2 1 0 +; ------- +; +; 0 0 X X \ Single decay then off. +; \______________________ Used by W0 PLAY command. +; +; +; 0 1 X X /| Single attack then off. +; / |_____________________ Used by W1 PLAY command. +; +; +; 1 0 0 0 \ |\ |\ |\ |\ |\ |\ |\ | Repeated decay. +; \| \| \| \| \| \| \| \| Used by W4 PLAY command. +; +; +; 1 0 0 1 \ Single decay then off. +; \______________________ Not used PLAY command (use W0 instead). +; +; +; 1 0 1 0 \ /\ /\ /\ /\ /\ / Repeated decay-attack. +; \/ \/ \/ \/ \/ \/ Used by W7 PLAY command. +; +; _____________________ +; 1 0 1 1 \ | Single decay then hold. +; \| Used by W2 PLAY command. +; +; +; 1 1 0 0 /| /| /| /| /| /| /| /| Repeated attack. +; / |/ |/ |/ |/ |/ |/ |/ | Used by W5 PLAY command. +; +; ______________________ +; 1 1 0 1 / Single attack then hold. +; / Used by W3 PLAY command. +; +; +; 1 1 1 0 /\ /\ /\ /\ /\ /\ Repeated attack-decay. +; / \/ \/ \/ \/ \/ \ Used by W6 PLAY command. +; +; +; 1 1 1 1 /| Single attack then off. +; / |_____________________ Not used by PLAY command (use W1 instead). +; +; +; -->| |<-- Envelope Period +; +; ----------- +; Register 14 (I/O Port) +; ----------- +; This controls the RS232 and Keypad sockets. +; Once the register has been selected, it can be read via port #FFFD and written via port #BFFD. +; +; Bit 0: KEYPAD CTS (out) - 0=Spectrum ready to receive, 1=Busy +; Bit 1: KEYPAD RXD (out) - 0=Transmit high bit, 1=Transmit low bit +; Bit 2: RS232 CTS (out) - 0=Spectrum ready to receive, 1=Busy +; Bit 3: RS232 RXD (out) - 0=Transmit high bit, 1=Transmit low bit +; Bit 4: KEYPAD DTR (in) - 0=Keypad ready for data, 1=Busy +; Bit 5: KEYPAD TXD (in) - 0=Receive high bit, 1=Receive low bit +; Bit 6: RS232 DTR (in) - 0=Device ready for data, 1=Busy +; Bit 7: RS232 TXD (in) - 0=Receive high bit, 1=Receive low bit +; +; The RS232 port also doubles up as a MIDI port, with communications to MIDI devices occurring at 31250 baud. +; Commands and data can be sent to MIDI devices. Command bytes have the most significant bit set, whereas data bytes have it reset. + + +; =============== +; Socket Pin Outs +; =============== + +; ----------------- +; RS232/MIDI Socket +; ----------------- +; The RS232/MIDI socket is controlled by register 14 of the AY-3-8912 sound generator. +; _____________ +; _| | +; | | Front View +; |_ 6 5 4 3 2 1 | +; |_|_|_|_|_|_|_| +; +; Pin Signal +; --- ------ +; 1 0V +; 2 TXD - In (Bit 7) +; 3 RXD - Out (Bit 3) +; 4 DTR - In (Bit 6) +; 5 CTS - Out (Bit 2) +; 6 12V + +; ------------- +; Keypad Socket +; ------------- +; The keypad socket is controlled by register 14 of the AY-3-8912 sound generator. +; Only bits 0 and 5 are used for communications with the keypad (pins 2 and 5). +; Writing a 1 to bit 0 (pin 2) will eventually force the keypad to reset. +; Summary information about the keypad and its communications protocol can be found in the Spectrum 128 Service Manual and +; a detailed description can be found at www.fruitcake.plus.com. +; _____________ +; _| | +; | | Front View +; |_ 6 5 4 3 2 1 | +; |_|_|_|_|_|_|_| +; +; Pin Signal +; --- ------ +; 1 0V +; 2 OUT - Out (Bit 0) +; 3 n/u - In (Bit 4) +; 4 n/u - Out (Bit 1) +; 5 IN - In (Bit 5) +; 6 12V +; +; n/u = Not used for keypad communications. +; +; The keypad socket was later used by Amstrad to support a lightgun. There are no routines within the ROMs to handle communication +; with the lightgun so each game has to implement its own control software. Only bits 4 and 5 are used for communication with the lightgun (pins 3 and 5). +; The connections to the lightgun are as follows: +; +; Pin Signal +; --- ------ +; 1 0V +; 2 n/u - Out (Bit 0) +; 3 SENSOR - In (Bit 4) +; 4 n/u - Out (Bit 1) +; 5 TRIGGER - In (Bit 5) +; 6 12V +; +; n/u = Not used for lightgun communication. + +; -------------- +; Monitor Socket +; -------------- +; +; ******* +; *** *** +; ** ** +; * --7-- --6-- * +; * | * +; * --3-- 8 --1-- * Front View +; * | * +; * / \ * +; * 5 | 4 * +; * / 2 \ * +; ** | ** +; *** *** +; ******* +; +; Pin Signal Level +; --- ------ ----- +; 1 Composite PAL 1.2V pk-pk (75 Ohms) +; 2 0 Volts 0V +; 3 Bright Output TTL +; 4 Composite Sync TTL +; 5 Vertical Sync TTL +; 6 Green TTL +; 7 Red TTL +; 8 Blue TTL +; +; A detailed description of the monitor socket and circuitry, and how to construct a suitable RGB SCART cable +; can be found at www.fruitcake.plus.com. + +; -------------- +; Edge Connector +; -------------- +; +; Pin Side A Side B +; --- ------ ------ +; 1 A15 A14 +; 2 A13 A12 +; 3 D7 +5V +; 4 n/u +9V +; 5 Slot Slot +; 6 D0 0V +; 7 D1 0V +; 8 D2 /CLK +; 9 D6 A0 +; 10 D5 A1 +; 11 D3 A2 +; 12 D4 A3 +; 13 /INT /IORQULA +; 14 /NMI 0V +; 15 /HALT n/u (On 48K Spectrum = VIDEO) +; 16 /MREQ n/u (On 48K Spectrum = /Y) +; 17 /IORQ n/u (On 48K Spectrum = V) +; 18 /RD n/u (On 48K Spectrum = U) +; 19 /WR /BUSREQ +; 20 -5V /RESET +; 21 /WAIT A7 +; 22 +12V A6 +; 23 -12V A5 +; 24 /M1 A4 +; 25 /RFSH /ROMCS +; 26 A8 /BUSACK +; 27 A10 A9 +; 28 n/u A11 +; +; Side A=Component Side, Side B=Underside. +; n/u = Not used. + diff --git a/SP_TRDOS.ASM b/SP_TRDOS.ASM new file mode 100644 index 0000000..2b35ae2 --- /dev/null +++ b/SP_TRDOS.ASM @@ -0,0 +1,7034 @@ +;╔════════════════════════════╤═════════════════════════════╗ +;║ TR-DOS ver. 5.04Em │ ELSY Co. 23.11.95 ║ +;╟────────────────────────────┴─────────────────────────────╢ +;║ RE-SOURCE'd by Z80MU PROFESSIONAL version 5.2b ║ +;╚══════════════════════════════════════════════════════════╝ + DEVICE ZXSPECTRUM128 + ORG 0 + +;*************************************** + INCLUDE 'INC/SPRINT00.INC' +;*************************************** +;CNF_PORT EQU #74 +AUTO_4000 EQU 04000H +AUTO_4010 EQU 04010H +AUTO_4011 EQU 04011H +AUTO_4020 EQU 04020H +AUTO_4080 EQU 04080H +AUTO_40E1 EQU 040E1H +AUTO_40E3 EQU 040E3H +AUTO_40E4 EQU 040E4H +AUTO_40E5 EQU 040E5H +AUTO_40F0 EQU 040F0H +AUTO_40F1 EQU 040F1H +AUTO_40FF EQU 040FFH +AUTO_4100 EQU 04100H +AUTO_4101 EQU 04101H +AUTO_4102 EQU 04102H +AUTO_4103 EQU 04103H +AUTO_4110 EQU 04110H +AUTO_4111 EQU 04111H +AUTO_4118 EQU 04118H +AUTO_4119 EQU 04119H +AUTO_411B EQU 0411BH +AUTO_411D EQU 0411DH +AUTO_411E EQU 0411EH +AUTO_4130 EQU 04130H +AUTO_4140 EQU 04140H +AUTO_41FF EQU 041FFH +AUTO_5220 EQU 05220H +AUTO_58E8 EQU 058E8H + +AUTO_5E34 EQU 05E34H +AUTO_5F00 EQU 05F00H +AUTO_5F10 EQU 05F10H +AUTO_5F12 EQU 05F12H +AUTO_6000 EQU 06000H +PORT_128 EQU 07FFDH +AUTO_A1DB EQU 0A1DBH +AUTO_B0ED EQU 0B0EDH +AUTO_B8ED EQU 0B8EDH +AUTO_C000 EQU 0C000H +AUTO_C130 EQU 0C130H +AUTO_C308 EQU 0C308H +AUTO_C916 EQU 0C916H +AUTO_FE15 EQU 0FE15H +AUTO_FF00 EQU 0FF00H +AUTO_FFFF EQU 0FFFFH + +;**************************************** + + DISPLAY "Start." +START: + DI + LD DE,0FFFFh + LD A,7 + JR CONT_1 +RST08: + NOP +CONT_1: + OUT (0FEH),A + LD A,'?' + JR CONT_2 + NOP +RST10: + JP PRINT_SYM +CONT_2: + LD I,A + JP CONT_3 +PRINT_LN: + JP PRINT_L +CONT_3: + NOP + NOP + NOP +AUTO_001E: + JR CONT_4 +RST20: + JP RST20_EXE + RET +CONT_4: + LD H,D + LD L,E + JR CONT_5 +RST28: + JP RST28_EXE +CONT_5: + LD (HL),2 + DEC HL + CP H + JR NZ,CONT_5 + JR CONT_6 +REDIR_RST38: +; JP RST38X +; EI +; RET + RST 38H + RST 38H + RST 38H + RST 38H + RST 38H +R38: + EI + RET +CONT_6: + OR A + SBC HL,DE + ADD HL,DE + INC HL + JR NC,CONT_7 + DEC (HL) + JR Z,CONT_7 + DEC (HL) + JR Z,CONT_6 +CONT_7: + DEC HL + LD (P_RAMTOP),HL + LD DE,3EAFh + LD BC,00A8h + LD A,E + EX DE,HL + LD SP,6000h + LD (AUTO_5F00),HL + LD HL,RET_1 + PUSH HL + LD HL,3D2Fh + PUSH HL + LD HL,0B8EDh +AUTO_0064: + JR CONT_8 + +;********** MAGIC *********** +; JP MAGIC_1 ;AUTO_2A56 + RETN + NOP +;********************************* +CONT_8: + LD (AUTO_5F10),HL + PUSH AF + LD A,201 + LD (AUTO_5F12),A + POP AF + LD HL,(AUTO_5F00) + JP AUTO_5F10 +RET_1: + EX DE,HL + INC HL + LD (UDG),HL + DEC HL + LD BC,AUTO_0540 + LD (ERR_BEEP),BC + LD (TOP_CLEAR),HL + LD HL,03C00h + LD (ZG),HL + LD HL,(TOP_CLEAR) + LD (HL),'>' + DEC HL + LD SP,HL + DEC HL + DEC HL + LD (ERR_SP),HL + LD DE,01303h + PUSH DE + IM 1 + LD IY,ERR_NR + LD HL,BEG_ADRESS +AUTO_00A8: + LD (CHANS),HL + LD DE,AUTO_15AF + LD BC,15h + EX DE,HL + CALL AUTO_0117 + EX DE,HL + DEC HL + LD (DATA_ADR),HL + INC HL + LD (BAS_PROG),HL + LD (BAS_VARS),HL + LD (HL),128 + INC HL + LD (E_LINE),HL + DB 54 +AUTO_00C8: + DEC C + INC HL + LD (HL),128 + INC HL + LD (WORK_SP),HL + LD (STK_BOT),HL + LD (STK_END),HL + LD A,'8' + LD (ATTR_P),A + LD (ATTR_T),A + LD (BORDER),A + LD HL,0223h + LD (KEY_TIME),HL + DEC (IY-58) + DEC (IY-54) + LD HL,015C6h + LD DE,REP_K_TYME + LD BC,14 + CALL AUTO_0117 + SET 1,(IY+1) + LD HL,RET_INS +AUTO_0100: + LD (HL),201 + RST RST20 + DW 00EDFH + LD HL,L_SCR_SIZE + LD (HL),2 + LD HL,AUTO_128B + PUSH HL + LD A,170 + LD (PR_BUFER),A + EI + JP AUTO_3D31 +AUTO_0117: + LD (AUTO_5F00),HL + LD HL,AUTO_3D2F + PUSH HL + LD HL,AUTO_B0ED + LD (AUTO_5F10),HL +AUTO_0124: + LD HL,(AUTO_5F00) + JP AUTO_5F10 +AUTO_012A: + CALL AUTO_20E5 + CALL AUTO_1D97 + LD HL,(E_LINE) + INC HL + LD E,(HL) + INC HL + LD D,(HL) + LD A,D + OR E + EX DE,HL + JR Z,AUTO_0140 + XOR A + LD (ERR_3D00),A +AUTO_0140: + PUSH HL + CALL AUTO_0232 + POP HL + LD (NEW_PPC),HL + XOR A + LD (NEW_S_PPC),A + RST RST20 + DW 016B0H + LD HL,(BAS_PROG) + DEC HL + LD (DATA_ADR),HL + LD SP,(ERR_SP) + LD A,(ERR_3D00) + OR A + LD HL,AUTO_1B76 + JR Z,AUTO_0166 + RST RST20 + DW 01BB0H +AUTO_0166: + PUSH HL + LD HL,RET_INS + PUSH HL + RET +AUTO_016C: ; вход через 3D03 + CALL xchg_intf1 + CALL BUFER_ON + LD A,255 + LD (MSG_FLAG),A + XOR A + LD (DOS_FLAG),A + LD A,170 + LD (FLAG_BOOT),A + LD HL,AUTO_0201 + LD (ADR_RET),HL + LD HL,0 + ADD HL,SP + LD (DOS_SP),HL + DEC HL + DEC HL + LD SP,HL + CALL AUTO_021D + LD HL,(TOP_CLEAR) + LD DE,(CH_ADR) + SBC HL,DE + EX DE,HL + JR NC,AUTO_01A5 + OR A + LD DE,101h + SBC HL,DE +AUTO_01A5: + LD (CH_ADR),HL +AUTO_01A8: + CALL AUTO_01C7 +AUTO_01AB: + JP Z,AUTO_01D3 + CP 234 + INC HL + JR NZ,AUTO_01A8 + CALL AUTO_01C7 + JR Z,AUTO_01AB + CP ':' + JP NZ,AUTO_01D3 + INC HL + CALL AUTO_3048 ; convert words + LD HL,(ADR_DOS_COM) + JP AUTO_030A +AUTO_01C7: + LD A,(HL) + CP 13 + RET Z + CP 128 + RET Z + OR A + RET + CALL AUTO_1E43 +AUTO_01D3: + LD HL,0 + LD (DISK_1_FLG),HL + CALL AUTO_20E5 + CALL AUTO_1D63 + LD HL,FLAG_BOOT + LD (HL),170 + LD HL,FL_NUMBER+1 + LD A,(HL) + OR A + LD (HL),0 + JR NZ,AUTO_01F3 + CALL AUTO_1E1C + CALL AUTO_0212 +AUTO_01F3: + LD SP,(DOS_SP) + LD HL,(ADR_RET) + LD BC,(DOS_ERR_2) + LD B,0 +AUTO_0200: + JP (HL) +AUTO_0201: + CALL AUTO_0232 + BIT 7,(IY+0) + RET NZ + LD DE,RET_INS + LD SP,(ERR_SP) + PUSH DE + RET +AUTO_0212: + CALL AUTO_1D8C + CP 13 + RET Z + CALL BASIC_RST20 + JR AUTO_0212 +AUTO_021D: + LD HL,(ERR_SP) + LD (ERR_SP_COPY),HL + LD HL,(DOS_SP) + DEC HL + DEC HL + LD (ERR_SP),HL + LD DE,AUTO_3D16 + LD (HL),E + INC HL + LD (HL),D + RET +AUTO_0232: + LD HL,(ERR_SP_COPY) + LD (ERR_SP),HL + RET +AUTO_0239: ; вход через 3D00 + LD HL,0 + LD (DOS_FLAG),HL + ADD HL,SP + LD (DOS_SP),HL + DEC HL + DEC HL + LD SP,HL + CALL LOAD_CR + LD HL,FLAG_BOOT + LD A,(HL) + CP 170 + LD A,0 + LD (DOS_ERR_2),A + JP Z,AUTO_02CB + LD (HL),170 + CALL AUTO_1D97 + CALL OPEN_CH2 + LD HL,NEW_NAME + RST PRINT_LN + CALL INIT_2X80 + LD A,(BEG_ADRESS) + CP 244 + JR Z,AUTO_0271 + LD HL,AUTO_1000 +AUTO_0270: + RST PRINT_LN +AUTO_0271: + LD A,(PR_BUFER) + CP 170 + JR NZ,AUTO_02CB + CALL xchg_intf1 +AUTO_027B: + LD HL,(E_LINE) + LD A,254 +AUTO_0280: + LD (BAS_DOS_FLG),A + LD (HL),247 + INC HL + LD (HL),'"' + INC HL + LD (HL),'b' + INC HL + LD (HL),'o' + INC HL + LD (HL),'o' + INC HL + LD (HL),'t' + INC HL + LD (HL),'"' + INC HL +CMD_NEW_EXEC: + LD (K_CUR),HL + LD (HL),13 + INC HL + LD (HL),128 + INC HL + LD (WORK_SP),HL + LD (STK_BOT),HL + LD (STK_END),HL + SET 3,(IY+1) + JR AUTO_02EF +AUTO_02B0: + LD B,3 +AUTO_02B2: + LD A,(HL) + LD (DE),A + INC HL + INC DE + DJNZ AUTO_02B2 + RET +AUTO_02B9: + LD B,' ' +AUTO_02BB: + PUSH BC + XOR 8 + OUT (P_DOS_FF),A + PUSH AF + LD A,5 + CALL pause_1ms + POP AF + POP BC + DJNZ AUTO_02BB + RET +AUTO_02CB: + LD HL,(DOS_SP) + DEC HL + DEC HL + LD SP,HL + CALL xchg_intf1 + CALL OPEN_CH0 + LD A,(PDOS_COPY) + OR 3 + CALL AUTO_02B9 + LD A,(PDOS_COPY) + CALL AUTO_02B9 + XOR A + LD (MSG_FLAG),A + CALL INPUT_LINE + CALL AUTO_3032 ; ПРЕОБРАЗОВАНИЕ СТРОКИ +AUTO_02EF: + CALL AUTO_1D9F ; ФУНКЦИЯ BASIC ??? ( CLS LOW SCR ) + LD HL,AUTO_02CB + LD (ADR_RET),HL + XOR A + LD (DOS_ERR_2),A + LD HL,(E_LINE) + PUSH HL + LD DE,COM_LN_COPY + CALL AUTO_02B0 + POP HL + LD (ADR_DOS_COM),HL +AUTO_030A: + LD A,(HL) + LD B,A + AND 128 + LD A,B + JR Z,AUTO_031A ; СТРОКА БЕЗ СЛОВА-КОМАНДЫ + CP 254 ; RETURN + JR Z,AUTO_031A + PUSH AF + CALL AUTO_3DC8 ; SET CONT_DISK + POP AF +AUTO_031A: + LD HL,AUTO_2FF3 ; СПИСОК КОМАНД + DEC HL + LD C,0 +AUTO_0320: + INC C + LD D,A + LD A,22 ; 21 !!! Add new comand !! + CP C + JP C,AUTO_01D3 ; ЕСЛИ НЕ НАЙДЕНО-ВЫЙТИ + LD A,D + INC HL + CP (HL) + JR NZ,AUTO_0320 + CP 254 + CALL NZ,BUFER_ON ; Включить буфер, если не RETURN + LD A,9 + LD (S_NAME_NUM),A + XOR A + LD (DOS_ERR_2),A + LD (DOS_ERROR),A + LD (ERR_3D00),A + LD HL,FLAGS + RES 7,(HL) + LD B,0 + LD HL,AUTO_3008 + DEC C + SLA C + ADD HL,BC + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL + PUSH HL + LD DE,AUTO_0359 + PUSH DE + JP (HL) +AUTO_0359: + LD HL,FLAGS + SET 7,(HL) + POP HL + JP (HL) +NAME_TRD: + DB 127," 1986 Technology Research. " + DB "version 5.05 by Makarchenco Ivan. " + DB " ",0,0 +AUTO_03AC: + CALL AUTO_03FD + CALL PRINT_CR + CALL PRINT_CR +AUTO_03B5: + LD BC,(FREE_SEC) + CALL AUTO_1DA9 + LD HL,AUTO_29D2 + RST PRINT_LN +AUTO_03C0: + JP AUTO_01D3 +AUTO_03C3: + PUSH AF + LD A,(BAS_DOS_FLG) + CP 254 + JR NZ,AUTO_03CD + POP AF + RET +AUTO_03CD: + POP AF + LD (DOS_ERR_2),A + LD A,(MSG_FLAG) + OR A + CALL Z,PRINT_L + RET +AUTO_03D9: + LD HL,AUTO_2A28 + LD A,1 + JP AUTO_1C4A +AUTO_03E1: + LD HL,AUTO_2766 +AUTO_03E4: + XOR A + JP AUTO_1C4A +AUTO_03E8: + XOR A + LD (CAT_SEC),A +AUTO_03EC: + LD DE,(CAT_SEC) + LD D,0 +AUTO_03F2: + CALL BUFER_ON + LD HL,BUFER + LD B,1 + JP READ_DISK +AUTO_03FD: + CALL BUFER_ON + LD DE,8 + JR AUTO_03F2 +RD_HEAD_DSK: + CALL AUTO_03FD + LD A,(CODE_10H) + CP 16 + JR Z,AUTO_0415 + LD HL,DISK_ERROR_MSG + RST PRINT_LN + JR AUTO_03C0 +AUTO_0415: + CALL get_disk_type + RES 0,(HL) + RES 1,(HL) + LD A,(TYPE_DISK) + BIT 0,A + JR NZ,AUTO_0425 + SET 0,(HL) +AUTO_0425: + BIT 3,A + RET NZ + SET 1,(HL) + RET +TEST_CR: + LD HL,(ADR_DOS_COM) + INC HL + LD A,(HL) + CP 13 + RET +; +; +; +CMD_CAT: + CALL TEST_CR + LD BC,2 + LD (MED_LEN),BC + JR Z,AUTO_046A + CP '#' + JR NZ,AUTO_045D + + LD (CH_ADR),HL ; КАНАЛ ДЛЯ CAT + CALL AUTO_1E0B + CALL AUTO_1D8C + CP 13 + JR Z,AUTO_046A + CP ',' + JP NZ,CMD_ERROR + CALL BASIC_RST20 + CALL AUTO_1DBD + JR AUTO_0460 + +AUTO_045D: + CALL INPUT_TXT +AUTO_0460: + CALL SINTAX_RET + CALL BAS_2BF1 + EX DE,HL + CALL SET_DRIVE +AUTO_046A: + CALL SINTAX_RET + LD A,(OPER_DISK) + LD (DISK_2_FLG),A + CALL RD_HEAD_DSK + LD A,(MED_LEN) +AUTO_0479: + CP 2 + PUSH AF + CALL Z,AUTO_1D97 + POP AF + CP 17 + JP NC,CMD_ERROR + CALL AUTO_1D84 + LD A,255 + LD (DISK_1_FLG),A + LD HL,AUTO_29F7 + RST PRINT_LN + LD HL,DISK_NAME + RST PRINT_LN + CALL PRINT_CR + LD A,(N_FILES) + LD HL,N_DEL_FL + SUB (HL) + PUSH HL + CALL AUTO_1DA3 + LD HL,AUTO_2A2B + RST PRINT_LN + POP HL + LD C,(HL) + CALL AUTO_1DA4 + LD HL,AUTO_2A1D + RST PRINT_LN + CALL AUTO_03E8 + LD HL,BUFER +AUTO_04B6: + CALL AUTO_04F6 + CALL PRINT_CR + LD A,(OPER_DISK) + ADD A,'A' + RST RST10 + LD B,2 +AUTO_04C4: + CALL AUTO_04F6 + PUSH BC + LD A,':' + RST RST10 + PUSH HL + CALL AUTO_2938 + LD BC,CONT_1+4 + POP HL + PUSH HL + ADD HL,BC + LD C,(HL) + PUSH BC + LD A,C + LD B,2 + CP 10 + JR C,AUTO_04DF + DEC B +AUTO_04DF: + CP 'd' + JR NC,AUTO_04E8 +AUTO_04E3: + LD A,' ' + RST RST10 + DJNZ AUTO_04E3 +AUTO_04E8: + POP BC + CALL AUTO_1DA9 + POP HL + POP BC + LD DE,RST10 + ADD HL,DE + DJNZ AUTO_04C4 + JR AUTO_04B6 +AUTO_04F6: + PUSH HL + PUSH BC + LD A,(DISK_2_FLG) + LD HL,OPER_DISK + CP (HL) + CALL NZ,SET_DISK + POP BC + POP HL + JP AUTO_2FC6 +AUTO_0507: + LD DE,RST10 + ADD HL,DE + RET +AUTO_050C: + PUSH HL + PUSH BC + LD BC,AUTO_A1DB + ADD HL,BC + JR C,AUTO_0517 + POP BC + POP HL + RET +AUTO_0517: + LD HL,CAT_SEC + INC (HL) + CALL AUTO_03EC + POP BC + POP HL + LD HL,BUFER + RET +GET_DISK: + AND 223 + SBC A,'A' + JP C,CMD_ERROR + CP 4 + JP NC,CMD_ERROR + RET +AUTO_0531: + CALL BAS_2BF1 + LD A,C + CP B + JP Z,CMD_ERROR + RET +; +; +; +CMD_NEW: + CALL AUTO_1DCD + CALL SINTAX_RET +AUTO_0540: + CALL AUTO_102E + CALL AUTO_1CB0 + LD A,(OPER_DISK) + LD (DISK_1_FLG),A + JP NZ,AUTO_03D9 + PUSH BC + CALL AUTO_165D + CALL AUTO_1CB0 + PUSH AF + LD A,(DISK_1_FLG) + LD HL,OPER_DISK + CP (HL) + JP NZ,CMD_ERROR + CALL RD_HEAD_DSK + POP AF + JP Z,AUTO_1C50 + POP BC +AUTO_0569: + CALL AUTO_166B + CALL AUTO_1E43 + JP AUTO_03E1 +AUTO_0572: + LD A,(ERR_3D00) + OR A + RET +AUTO_0577: + LD A,(N_DEL_FLS) + OR A + JP Z,AUTO_03D9 + JP AUTO_03E1 +AUTO_0581: + PUSH BC + CALL AUTO_1D97 + LD A,(OPER_DISK) + ADD A,'A' + CALL PRINT_SYM + LD A,':' + CALL PRINT_SYM + LD HL,FL_NAME + CALL AUTO_2938 + LD HL,AUTO_2820 + CALL PRINT_L + CALL AUTO_1052 + CP 'Y' + PUSH AF + CALL AUTO_1D97 + POP AF + POP BC + RET NZ + PUSH BC + CALL AUTO_1D97 + POP BC + CALL AUTO_0781 + XOR A + RET +AUTO_05B4: + LD A,(FL_TYPE) + CP '#' + JR Z,AUTO_05BD + XOR A + RET +AUTO_05BD: + LD A,10 + LD (S_NAME_NUM),A + CALL AUTO_1CB3 + LD A,9 + LD (S_NAME_NUM),A + RET +AUTO_05CB: + LD A,(FL_NAME) + CP '*' + JP NZ,AUTO_03D9 + CALL BAS_2BF1 + EX DE,HL + CALL SET_DRIVE + LD A,(HL) + CP '*' + JP NZ,CMD_ERROR + LD A,(OPER_DISK) + LD (DISK_2_FLG),A + LD A,(DISK_2_FLG) + CALL SET_DISK + CALL RD_HEAD_DSK + LD A,255 + LD (BUF_FLAG+1),A +AUTO_05F4: + LD A,(DISK_1_FLG) + CALL SET_DISK + CALL RD_HEAD_DSK + LD A,(BUF_FLAG+1) + INC A + LD (BUF_FLAG+1),A + LD C,A + CALL AUTO_165D + LD A,(FL_NAME) + CP 0 + JP Z,AUTO_03E1 + CP 1 + JR Z,AUTO_05F4 + LD HL,FL_START + LD DE,VAR_2 + LD BC,START+7 + LDIR + LD A,(DISK_2_FLG) + CALL SET_DISK + CALL AUTO_1CB3 + JR NZ,AUTO_0634 + CALL AUTO_05B4 + JR NZ,AUTO_0634 + CALL AUTO_0581 + JR NZ,AUTO_05F4 +AUTO_0634: + CALL AUTO_063C + CALL AUTO_1E43 + JR AUTO_05F4 +AUTO_063C: + CALL AUTO_03FD + LD A,(N_FILES) + CP 128 + JP Z,AUTO_1C45 + LD HL,VAR_2 + LD DE,FL_START + LD BC,START+7 + LDIR + LD DE,(FL_SIZE) + LD D,0 + OR A + LD HL,(FREE_SEC) + SBC HL,DE + JP C,AUTO_1C45 + LD (FREE_SEC),HL + LD HL,(CLEAR_SEC) + LD (FL_PLACE),HL + PUSH HL + CALL AUTO_072F + POP HL + LD (FL_PLACE),HL + LD HL,(CONT_SEC) + LD (CLEAR_SEC),HL + LD HL,N_FILES + INC (HL) + LD C,(HL) + DEC C + LD B,0 + PUSH BC + LD DE,CONT_1 + LD (CONT_SEC),DE + CALL AUTO_1E43 + POP BC + CALL AUTO_166B + RET +; +; +; +CMD_COPY: + LD HL,(ADR_DOS_COM) + INC HL + LD A,(HL) + AND 223 + CP 'S' + JP Z,AUTO_1360 + CP 'B' + JP Z,AUTO_152C + CALL AUTO_1DCD + CALL SINTAX_RET + CALL AUTO_166F + CALL AUTO_102E + CALL AUTO_1CB0 + LD A,(OPER_DISK) + LD (DISK_1_FLG),A + JP NZ,AUTO_05CB + CALL AUTO_165D + LD HL,FL_START + LD DE,VAR_2 + LD BC,START+7 + LDIR + CALL AUTO_1CB0 + PUSH AF + PUSH BC + LD A,(OPER_DISK) + LD (DISK_2_FLG),A + LD A,(DISK_1_FLG) + CALL SET_DISK + CALL RD_HEAD_DSK + LD A,(DISK_2_FLG) + CALL SET_DISK + CALL RD_HEAD_DSK + POP BC + POP AF + JR NZ,AUTO_06F3 + CALL AUTO_05B4 + JR NZ,AUTO_06F3 + CALL AUTO_0581 + JP NZ,AUTO_03E1 +AUTO_06F3: + CALL AUTO_063C + CALL AUTO_1E43 + LD A,(FL_TYPE) + CP '#' + JP NZ,AUTO_03E1 + LD A,10 + LD (S_NAME_NUM),A + LD HL,FL_START + INC (HL) + LD A,(DISK_1_FLG) + CALL SET_DISK + CALL AUTO_05B4 + JP NZ,AUTO_03E1 +AUTO_0716: + CALL AUTO_165D + LD HL,FL_START + LD DE,VAR_2 + LD BC,START+7 + LDIR + LD A,(DISK_2_FLG) + CALL SET_DISK + CALL RD_HEAD_DSK + JR AUTO_06F3 +AUTO_072F: + LD A,(VAR_2_0) + OR A + RET Z + PUSH HL + LD HL,L_5D23 + SUB (HL) + POP HL + JR NC,AUTO_0775 + LD A,(VAR_2_0) + LD B,A + XOR A + LD (VAR_2_0),A +AUTO_0744: + PUSH BC + LD A,(DISK_1_FLG) + CALL SET_DISK + POP BC + PUSH BC + LD HL,(VAR_1) + PUSH HL + LD DE,(VAR_2_1) + CALL READ_DISK + LD HL,(CONT_SEC) + LD (VAR_2_1),HL + LD A,(DISK_2_FLG) + CALL SET_DISK + POP HL + POP BC + LD DE,(FL_PLACE) + CALL WRITE_DISK + LD HL,(CONT_SEC) + LD (FL_PLACE),HL + JR AUTO_072F +AUTO_0775: + LD (VAR_2_0),A + PUSH HL + LD HL,L_5D23 + LD B,(HL) + POP HL + XOR A + JR AUTO_0744 +AUTO_0781: + XOR A + LD (N_DEL_FLS),A + JR AUTO_07A0 +; +; +; +CMD_ERASE: + CALL INPUT_TXT + CALL SINTAX_RET + CALL AUTO_102E + XOR A + LD (N_DEL_FLS),A + CALL AUTO_292F + CALL AUTO_07A0 + JP NZ,AUTO_0577 + JP AUTO_03E1 +AUTO_07A0: + LD A,(FL_NAME) + LD (FST_SYM_NAME),A + RET NZ + LD HL,N_DEL_FLS + INC (HL) + PUSH BC + CALL AUTO_03FD + LD A,(N_FILES) + POP BC + INC C + CP C + JR NZ,AUTO_07BC + DEC A + LD (N_FILES),A + XOR A +AUTO_07BC: + PUSH AF + JR Z,AUTO_07C3 + LD HL,N_DEL_FL + INC (HL) +AUTO_07C3: + PUSH BC + CALL AUTO_1E43 + POP BC + DEC C + CALL AUTO_165D + POP AF + JP Z,AUTO_07D2 + LD A,1 +AUTO_07D2: + LD (FL_NAME),A + PUSH AF + CALL AUTO_1E40 + LD A,(FST_SYM_NAME) + LD (FL_NAME),A + POP AF + JR Z,AUTO_07E7 + CALL AUTO_1CB3 + JR AUTO_07A0 + +; +AUTO_07E7: + BLOCK #07E7-$,255 + CALL AUTO_03FD + LD HL,(FL_PLACE) + LD (CLEAR_SEC),HL + LD DE,(FL_SIZE) + LD HL,(FREE_SEC) + LD D,0 + ADD HL,DE + LD (FREE_SEC),HL + JP AUTO_1E43 +WG93_EXE_: + AND 252 + JP WG93_EXE +;************************************** + DISPLAY "2k" + BLOCK #0840-$,255 + INCLUDE 'trdos/tr_msd_3.asz' + + + BLOCK #0B00-$,255 +ZG_RUS: + DB 3 ; идентификатор версии TR-DOS ??? +;************************************** + INCLUDE 'trdos/tr_msd_2.asz' + +; DSS 0E00h,255 + +;************************************** +; +; Русский знакогенератор - 40h..7Fh символы. +; +; DSS 0E00h,255 + +; DB 0,0,76,82,114,82,76,0,0,0,56,4,60,68 +; DB 60,0,0,0,120,64,120,68,120,0,0,0,72 +; DB 72,72,72,124,4,0,0,24,40,40,40,124,68 +; DB 0,0,56,68,120,64,60,0,0,16,56,84,84 +; DB 56,16,0,0,0,124,64,64,64,64,0,0,0,68 +; DB 40,16,40,68,0,0,0,68,76,84,100,68,0 +; DB 0,16,68,76,84,100,68,0,0,0,72,80,112 +; DB 72,68,0,0,0,28,36,36,36,68,0,0,0,68 +; DB 108,84,84,68,0,0,0,68,68,124,68,68,0 +; DB 0,0,56,68,68,68,56,0,0,0,124,68,68,68 +; DB 68,0,0,0,60,68,60,36,68,0,0,0,120,68 +; DB 68,120,64,64,0,0,56,64,64,64,56,0,0 +; DB 0,124,16,16,16,16,0,0,0,68,68,60,4,56 +; DB 0,0,0,84,84,56,84,84,0,0,0,120,68,120 +; DB 68,120,0,0,0,64,64,120,68,120,0,0,0 +; DB 68,68,116,76,116,0,0,0,56,68,24,68,56 +; DB 0,0,0,84,84,84,84,124,0,0,0,56,4,28 +; DB 4,56,0,0,0,84,84,84,84,126,2,0,0,68 +; DB 68,60,4,4,0,0,0,96,32,56,36,56,0,0,76 +; DB 82,114,82,82,76,0,0,60,66,66,126,66 +; DB 66,0,0,124,64,124,66,66,124,0,0,68,68 +; DB 68,68,68,126,2,0,24,36,36,36,36,126 +; DB 66,0,126,64,124,64,64,126,0,0,56,84 +; DB 84,56,16,16,0,0,126,64,64,64,64,64,0 +; DB 0,66,36,24,24,36,66,0,0,66,70,74,82 +; DB 98,66,0,0,90,70,74,82,98,66,0,0,68,72 +; DB 112,72,68,66,0,0,30,34,34,34,34,66,0 +; DB 0,66,102,90,66,66,66,0,0,66,66,126,66 +; DB 66,66,0,0,60,66,66,66,66,60,0,0,126 +; DB 66,66,66,66,66,0,0,62,66,66,62,34,66 +; DB 0,0,124,66,66,124,64,64,0,0,60,66,64 +; DB 64,66,60,0,0,254,16,16,16,16,16,0,0 +; DB 66,66,62,2,66,60,0,0,84,84,56,84,84 +; DB 84,0,0,124,66,124,66,66,124,0,0,64,64 +; DB 124,66,66,124,0,0,66,66,114,74,74,114 +; DB 0,0,60,66,12,2,66,60,0,0,84,84,84,84 +; DB 84,124,0,0,60,66,14,2,66,60,0,0,84,84 +; DB 84,84,84,126,2,0,66,66,66,126,2,2,0 +; DB 60,66,153,161,161,153,66,60 +; +;************************************** +;************************************** + BLOCK #1000 - $,255 + +;************************************** + DISPLAY "4k" + +AUTO_1000: + DB 22,9,5,"Interface one fitted",0 +; +; +; +CMD_STAR: + CALL INPUT_TXT + CALL SINTAX_RET + CALL AUTO_0531 + LD A,(DE) + CALL GET_DISK + LD (CONT_DISK),A + CALL SET_DISK + JP AUTO_03E1 +AUTO_102E: + LD B,'C' + LD A,(DOS_ERROR) + OR A + JR NZ,AUTO_104D + CALL AUTO_1D8C + CP 175 + LD B,'C' + JR Z,AUTO_104D + CP 228 + LD B,'D' + JR Z,AUTO_104D + CP '#' + LD B,'#' + JR Z,AUTO_104D + LD B,'B' +AUTO_104D: + LD HL,FL_TYPE + LD (HL),B + RET +AUTO_1052: + DI + PUSH HL + PUSH BC + PUSH DE +AUTO_1056: + RST RST20 + DW 0028EH +AUTO_1059: + LD C,0 + JR NZ,AUTO_1056 + RST RST20 + DW 0031EH + JR NC,AUTO_1056 + DEC D + LD E,A + RST RST20 + DW 00333H + POP DE + POP BC + POP HL + AND 223 + EI + RET +AUTO_106E: + LD HL,AUTO_58E8 + LD B,10 +AUTO_1073: + LD (HL),7 + INC HL + DJNZ AUTO_1073 + LD (HL),2 + INC HL + LD (HL),22 + INC HL + DB 54 +AUTO_107F: + INC (HL) + INC HL + LD (HL),'%' + INC HL + LD (HL),'(' + INC HL + LD (HL),7 + LD HL,AUTO_40F1 + LD B,8 + XOR A +AUTO_108F: + PUSH BC + SCF + RLA + PUSH HL + PUSH AF + LD B,5 +AUTO_1096: + INC HL + LD (HL),A + DJNZ AUTO_1096 + POP AF + POP HL + POP BC + LD DE,AUTO_0100 + ADD HL,DE + DJNZ AUTO_108F + RET + RET +AUTO_10A5: + DB " Del." +AUTO_10AA: + DB " File(s)",0 +AUTO_10B3: + DB "Title:",160 +AUTO_10BA: + DB 23,17," Disk Drive: ",0 +AUTO_10CA: + DB 23,16," ",0 +AUTO_10CE: + DB 23,16," 40 Track S. Side",0 +AUTO_10E2: + DB 23,16," 80 Track S. Side",0 +AUTO_10F6: + DB 23,16," 40 Track D. Side",0 +AUTO_110A: + DB 23,16," 80 Track D. Side",0 +AUTO_111E: + DB 23,16," Free Sector ",0 +AUTO_112E: + DB 13,13," File Name Start Length " + DB "Line",0 +AUTO_1151: + LD HL,(WORK_SP) + LD (VAR_1),HL + LD BC,AUTO_021D+5 + JP AUTO_1E23 +AUTO_115D: + XOR A + LD DE,AUTO_2710 +AUTO_1161: + SBC HL,DE + JR C,AUTO_1168 + INC A + JR AUTO_1161 +AUTO_1168: + ADD A,'0' + CALL AUTO_11A8 + ADD HL,DE + XOR A + LD DE,AUTO_03E8 +AUTO_1172: + SBC HL,DE + JR C,AUTO_1179 + INC A + JR AUTO_1172 +AUTO_1179: + ADD A,'0' + CALL AUTO_11A8 + ADD HL,DE + XOR A + LD DE,AUTO_0064 +AUTO_1183: + SBC HL,DE + JR C,AUTO_118A + INC A + JR AUTO_1183 +AUTO_118A: + ADD A,'0' + CALL AUTO_11A8 + ADD HL,DE + XOR A + LD DE,CONT_1+1 +AUTO_1194: + SBC HL,DE + JR C,AUTO_119B + INC A + JR AUTO_1194 +AUTO_119B: + ADD A,'0' + CALL AUTO_11A8 + ADD HL,DE + LD A,L + ADD A,'0' + CALL AUTO_11A8 + RET +AUTO_11A8: + PUSH HL + PUSH DE + CALL PRINT_SYM + POP DE + POP HL + RET +AUTO_11B0: + PUSH HL + PUSH BC + LD A,(DISK_2_FLG) + LD HL,OPER_DISK + CP (HL) + CALL NZ,SET_DISK + POP BC + POP HL + CALL AUTO_050C + LD A,(HL) + OR A + JP Z,AUTO_01D3 + CP 1 + CALL Z,AUTO_0507 + RET NZ + JR AUTO_11B0 +; +; +; +CMD_LIST: + CALL TEST_CR + LD BC,START+2 + LD (MED_LEN),BC + JR Z,AUTO_1205 + CP '#' + JR NZ,AUTO_11F8 + LD (CH_ADR),HL + CALL AUTO_1E0B + CALL AUTO_1D8C + CP 13 + JR Z,AUTO_1205 + CP ',' + JP NZ,CMD_ERROR + CALL BASIC_RST20 + CALL AUTO_1DBD + JR AUTO_11FB +AUTO_11F8: + CALL INPUT_TXT +AUTO_11FB: + CALL SINTAX_RET + CALL BAS_2BF1 + EX DE,HL + CALL SET_DRIVE +AUTO_1205: + CALL SINTAX_RET + LD A,(OPER_DISK) + LD (DISK_2_FLG),A + CALL RD_HEAD_DSK + LD A,(MED_LEN) + CP 2 + PUSH AF + CALL Z,AUTO_1D97 + POP AF + CP 17 + JP NC,CMD_ERROR + CALL AUTO_1D84 + LD A,255 + LD (DISK_1_FLG),A + CALL AUTO_1151 + LD HL,CLEAR_SEC + LD DE,(VAR_1) + LD BC,RST20 + LDIR + CALL AUTO_03E8 + LD HL,BUFER + PUSH HL +AUTO_123E: + LD HL,AUTO_10B3 + PUSH BC + RST PRINT_LN + LD HL,(VAR_1) + LD BC,CONT_2+1 + ADD HL,BC + RST PRINT_LN + LD HL,AUTO_10BA + RST PRINT_LN + LD A,(OPER_DISK) + ADD A,'A' + CALL PRINT_SYM + CALL PRINT_CR + LD HL,(VAR_1) + LD BC,START+3 + ADD HL,BC + LD A,(HL) + LD HL,(VAR_1) + LD BC,CONT_2 + ADD HL,BC + SUB (HL) + PUSH HL + CALL AUTO_1DA3 + LD HL,AUTO_10AA + RST PRINT_LN + LD HL,(VAR_1) + LD BC,START+2 + ADD HL,BC + LD A,(HL) + LD HL,AUTO_10CE + CP 25 + JR Z,AUTO_1292 + LD HL,AUTO_10E2 + CP 24 + JR Z,AUTO_1292 + LD HL,AUTO_10F6 +AUTO_128B: + CP 23 + JR Z,AUTO_1292 + LD HL,AUTO_110A +AUTO_1292: + RST PRINT_LN + POP HL + LD C,(HL) + CALL AUTO_1DA4 + LD HL,AUTO_10A5 + RST PRINT_LN + LD HL,AUTO_111E + RST PRINT_LN + LD HL,(VAR_1) + LD BC,START+4 + ADD HL,BC + LD C,(HL) + INC HL + LD B,(HL) + CALL AUTO_1DA9 + LD HL,AUTO_112E + RST PRINT_LN + POP BC + POP HL + LD B,16 +AUTO_12B5: + CALL AUTO_11B0 + CALL PRINT_CR + PUSH BC + PUSH HL + CALL AUTO_2938 + LD BC,CONT_1+4 + POP HL + PUSH HL + ADD HL,BC + LD C,(HL) + PUSH BC + LD A,C + LD B,2 + CP 10 + JR C,AUTO_12D0 + DEC B +AUTO_12D0: + CP 'd' + JR NC,AUTO_12D9 +AUTO_12D4: + LD A,' ' + RST RST10 + DJNZ AUTO_12D4 +AUTO_12D9: + POP BC + CALL AUTO_1DA9 + LD HL,AUTO_10CA + RST PRINT_LN + POP HL + PUSH HL + LD BC,CONT_1 + ADD HL,BC + LD E,(HL) + INC HL + LD D,(HL) + PUSH HL + EX DE,HL + CALL AUTO_115D + LD A,' ' + CALL PRINT_SYM + POP HL + INC HL + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL + CALL AUTO_115D + POP HL + PUSH HL + LD BC,RST08 + ADD HL,BC +AUTO_1303: + LD A,(HL) + CP 'B' + CALL Z,AUTO_131B + POP HL + POP BC + LD DE,RST10 + ADD HL,DE + DJNZ AUTO_12B5 + PUSH HL + CALL PRINT_CR + CALL PRINT_CR + JP AUTO_123E +AUTO_131B: + LD BC,START+5 + ADD HL,BC + LD B,(HL) + INC HL + LD E,(HL) + INC HL + LD D,(HL) + DEC B + JR Z,AUTO_1335 + DEC B + JR Z,AUTO_1335 + LD A,16 +AUTO_132C: + INC E + CP E + JR NZ,AUTO_1333 + LD E,0 + INC D +AUTO_1333: + DJNZ AUTO_132C +AUTO_1335: + LD HL,(VAR_1) + LD BC,RST20+1 + ADD HL,BC + LD B,2 + PUSH HL + CALL READ_DISK + LD A,128 + POP HL + LD BC,AUTO_0200 + CPIR + LD A,(HL) + CP 170 + RET NZ + INC HL + LD C,(HL) + INC HL + LD B,(HL) + LD A,B + OR C + RET Z + PUSH BC + LD A,' ' + CALL PRINT_SYM + POP BC + CALL AUTO_1DA9 + RET +AUTO_1360: + CALL AUTO_1DC5 + CALL BASIC_RST20 + CALL AUTO_1DBD + CALL SINTAX_RET + CALL AUTO_166F + LD HL,AUTO_27AA + CALL PRINT_L +AUTO_1375: + CALL AUTO_1052 + CP 'Y' + JR NZ,AUTO_1375 + CALL AUTO_1D9F + CALL AUTO_102E + CALL AUTO_1CB0 + JP NZ,AUTO_03D9 + CALL AUTO_13B7 + LD A,(FL_TYPE) + CP '#' + JP NZ,AUTO_03E1 +AUTO_1393: + LD A,10 + LD (S_NAME_NUM),A + LD HL,FL_START + INC (HL) + CALL AUTO_1D97 + LD HL,AUTO_27AA + CALL PRINT_L +AUTO_13A5: + CALL AUTO_1052 + CP 'Y' + JR NZ,AUTO_13A5 + CALL AUTO_1CB3 + JP NZ,AUTO_03E1 + CALL AUTO_13B7 + JR AUTO_1393 +AUTO_13B7: + CALL AUTO_165D + LD HL,FL_START + LD DE,VAR_2 + LD BC,START+7 + LDIR + LD A,(VAR_2_0) + LD (ERR_3D00),A + CALL RD_HEAD_DSK + CALL get_disk_type + LD (DOS_CH_ADR),A + LD A,255 + LD (COM_LN_COPY+1),A + CALL AUTO_1451 + LD HL,(FL_NUMBER+1) + LD (FL_PLACE),HL + LD HL,(CONT_SEC) + LD (CLEAR_SEC),HL + LD HL,N_FILES + INC (HL) + LD C,(HL) + DEC C + LD B,0 + PUSH BC + LD DE,CONT_1 + LD (CONT_SEC),DE + CALL AUTO_1E43 + POP BC + CALL AUTO_166B + CALL AUTO_1E43 + RET +AUTO_1403: + XOR A + LD (COM_LN_COPY+1),A + CALL RD_HEAD_DSK + CALL get_disk_type + LD (DOS_CH_ADR+1),A + CALL AUTO_1CB3 + JP Z,AUTO_1C50 + CALL AUTO_03FD + LD A,(N_FILES) + CP 128 + JP Z,AUTO_2723 + LD HL,VAR_2 + LD DE,FL_START + LD BC,START+7 + LDIR + CALL AUTO_03FD + LD A,(ERR_3D00) + LD (FL_SIZE),A + LD DE,(FL_SIZE) + LD D,0 + OR A + LD HL,(FREE_SEC) + SBC HL,DE + JP C,AUTO_1C45 + LD (FREE_SEC),HL + LD HL,(CLEAR_SEC) + LD (FL_PLACE),HL + LD (FL_NUMBER+1),HL + RET +AUTO_1451: + LD A,(VAR_2_0) + OR A + RET Z + LD A,(COM_LN_COPY+1) + OR A + JR NZ,AUTO_146F + CALL AUTO_1D97 + LD HL,AUTO_27AA + CALL PRINT_L +AUTO_1465: + CALL AUTO_1052 + CP 'Y' + JR NZ,AUTO_1465 + CALL AUTO_1D9F +AUTO_146F: + LD A,(VAR_2_0) + OR A + RET Z + PUSH HL + LD HL,L_5D23 + SUB (HL) + POP HL + JR NC,AUTO_14CB + LD A,(VAR_2_0) + LD B,A + XOR A + LD (VAR_2_0),A +AUTO_1484: + PUSH BC + LD (RD_WR_COM),A + LD HL,(VAR_1) + PUSH HL + LD DE,(VAR_2_1) + CALL AUTO_14D8 + CALL READ_DISK + LD HL,(CONT_SEC) + LD (VAR_2_1),HL + CALL AUTO_1D97 + LD HL,AUTO_2785 + CALL PRINT_L +AUTO_14A5: + CALL AUTO_1052 + CP 'Y' + JR NZ,AUTO_14A5 + CALL AUTO_1D9F + LD A,(COM_LN_COPY+1) + OR A + CALL NZ,AUTO_1403 + POP HL + POP BC + LD DE,(FL_PLACE) + CALL AUTO_14E4 + CALL WRITE_DISK + LD HL,(CONT_SEC) + LD (FL_PLACE),HL + JP AUTO_1451 +AUTO_14CB: + LD (VAR_2_0),A + PUSH HL + LD HL,L_5D23 + LD B,(HL) + POP HL + XOR A + JP AUTO_1484 +AUTO_14D8: + PUSH HL + PUSH DE + CALL get_disk_type + LD A,(DOS_CH_ADR) + LD (HL),A + POP DE + POP HL + RET +AUTO_14E4: + PUSH HL + PUSH DE + CALL get_disk_type + LD A,(DOS_CH_ADR+1) + LD (HL),A + POP DE + POP HL + RET +AUTO_14F0: + XOR A + LD (COM_LN_COPY+1),A + CALL RD_HEAD_DSK + CALL get_disk_type + LD (DOS_CH_ADR+1),A + LD A,(TYPE_DISK) + LD (FL_START+1),A + LD HL,AUTO_0280 + CP 25 + JR Z,AUTO_151F + LD HL,AUTO_04F6+10 + CP 24 + JR Z,AUTO_151F + CP 23 + JR Z,AUTO_151F + LD HL,0A00h + CP 22 + JR Z,AUTO_151F + JP CMD_ERROR +AUTO_151F: + LD (FL_NAME),HL + LD BC,(FL_N_2) + SBC HL,BC + JP C,AUTO_1C45 + RET +AUTO_152C: + CALL SINTAX_RET + CALL AUTO_166F + LD HL,AUTO_2779 + CALL PRINT_L + LD HL,AUTO_27AA + CALL PRINT_L +AUTO_153E: + CALL AUTO_1052 + CP 'Y' + JR NZ,AUTO_153E + CALL AUTO_1D9F + LD A,255 + LD (COM_LN_COPY+1),A + CALL RD_HEAD_DSK + CALL get_disk_type + LD (DOS_CH_ADR),A + LD A,(TYPE_DISK) + CP 25 + LD HL,AUTO_0280 + JR Z,AUTO_1575 + LD HL,AUTO_04F6+10 + CP 24 + JR Z,AUTO_1575 + CP 23 + JR Z,AUTO_1575 + LD HL,0A00h + CP 22 + JR Z,AUTO_1575 + JP CMD_ERROR +AUTO_1575: + LD BC,(FREE_SEC) + SBC HL,BC + LD (FL_TYPE),HL + LD (FL_N_2),HL + LD HL,START + LD (FL_N_4),HL + LD (FL_N_6),HL + CALL AUTO_15B8 + CALL RD_HEAD_DSK + LD A,(FL_START+1) + LD (TYPE_DISK),A + LD HL,(FL_NAME) + LD BC,(FL_N_2) + SBC HL,BC + LD (FREE_SEC),HL + CALL AUTO_14E4 + LD DE,CONT_1 + LD (CONT_SEC),DE + CALL AUTO_1E43 +AUTO_15AF: + JP AUTO_03E1 +AUTO_15B2: + LD HL,(FL_TYPE) + LD A,H + OR L + RET +AUTO_15B8: + CALL AUTO_15B2 + RET Z + LD A,(COM_LN_COPY+1) + OR A + JR NZ,AUTO_15DB + CALL AUTO_1D97 + LD HL,AUTO_2779 + CALL PRINT_L + LD HL,AUTO_27AA + CALL PRINT_L +AUTO_15D1: + CALL AUTO_1052 + CP 'Y' + JR NZ,AUTO_15D1 + CALL AUTO_1D9F +AUTO_15DB: + CALL AUTO_15B2 + RET Z + PUSH BC + PUSH HL + LD HL,L_5D23 + LD C,(HL) + LD B,0 + POP HL + SBC HL,BC + POP BC + JP NC,AUTO_1644 + LD BC,(FL_TYPE) + LD HL,START + LD (FL_TYPE),HL +AUTO_15F8: + PUSH BC + LD HL,(VAR_1) + PUSH HL + CALL AUTO_14D8 + LD DE,(FL_N_4) + LD B,C + CALL READ_DISK + LD HL,(CONT_SEC) + LD (FL_N_4),HL + CALL AUTO_1D97 + LD HL,AUTO_2779 + CALL PRINT_L + LD HL,AUTO_2785 + CALL PRINT_L +AUTO_161D: + CALL AUTO_1052 + CP 'Y' + JR NZ,AUTO_161D + CALL AUTO_1D9F + LD A,(COM_LN_COPY+1) +AUTO_162A: + OR A + CALL NZ,AUTO_14F0 + POP HL + POP BC + LD DE,(FL_N_6) + LD B,C + CALL AUTO_14E4 + CALL WRITE_DISK + LD HL,(CONT_SEC) + LD (FL_N_6),HL + JP AUTO_15B8 +AUTO_1644: + LD (FL_TYPE),HL + PUSH HL + LD HL,L_5D23 + LD C,(HL) + LD B,0 + POP HL + XOR A + JP AUTO_15F8 + CALL AUTO_165D + LD A,(FL_NAME) + CP 1 + RET +AUTO_165C: + LD C,A +AUTO_165D: + XOR A +AUTO_165E: + PUSH BC + CALL AUTO_17E9 + POP BC + RET +AUTO_1664: + LD C,A +AUTO_1665: + CALL AUTO_166B + JP AUTO_1E43 +AUTO_166B: + LD A,255 + JR AUTO_165E +AUTO_166F: + LD A,255 + LD (BAS_DOS_FLG),A + CALL AUTO_1680 + LD HL,(WORK_SP) + LD (VAR_1),HL + JP AUTO_1E23 +AUTO_1680: + RST RST20 + DW 01F1AH + LD HL,0FFFFh + SBC HL,BC + LD A,H + CP 16 + JR NC,AUTO_168F + LD A,17 +AUTO_168F: + DEC A + LD (L_5D23),A + LD B,A + LD C,0 + RET + LD (MED_START),HL + LD (MED_LEN),HL + LD DE,(FL_SIZE) + LD HL,(DOS_CH_ADR) + LD D,0 + ADD HL,DE + LD (DOS_CH_ADR),HL + RET +; +; +; +CMD_MOVE: + CALL TEST_CR + JP NZ,AUTO_1775 + CALL SINTAX_RET + CALL AUTO_166F + LD HL,(VAR_1) + LD (FL_N_4),HL + LD DE,0900h + ADD HL,DE + LD (VAR_1),HL + LD A,(L_5D23) + SUB 9 + LD (L_5D23),A + LD HL,(FL_N_4) + LD DE,START + LD B,9 + CALL READ_DISK + LD HL,(FL_N_4) + LD (FL_N_2),HL + LD HL,AUTO_0100 + LD (MED_START),HL + LD (FL_NAME),HL + XOR A + LD (FL_N_6),HL + LD (FL_N_7),A + CALL MOVE_X00 + LD DE,(FL_N_4) + LD HL,(FL_N_2) + EX DE,HL + LD BC,WG93_EXE_ + ADD HL,BC + AND A + SBC HL,DE + DEC HL + LD C,L + LD B,H + EX DE,HL + LD D,H + LD E,L + INC DE + LD (HL),0 + LDIR + LD HL,(FL_N_4) + LD DE,08E1h + ADD HL,DE + LD E,(HL) + INC HL + LD D,(HL) + LD BC,(MED_START) + LD (HL),B + DEC HL + LD (HL),C + INC HL + INC HL + INC HL + INC HL + LD C,(HL) + INC HL + LD B,(HL) + EX DE,HL + LD A,L + AND 15 + LD L,H + LD H,0 + ADD HL,HL + ADD HL,HL + ADD HL,HL + ADD HL,HL + ADD A,L + LD L,A + ADD HL,BC + LD BC,(MED_START) + LD (MED_START),HL + LD L,B + LD H,0 + ADD HL,HL + ADD HL,HL + ADD HL,HL + ADD HL,HL + LD A,C + AND 15 + ADD A,L + LD C,A + LD B,H + LD HL,(MED_START) + AND A + SBC HL,BC + EX DE,HL + LD (HL),D + DEC HL + LD (HL),E + DEC HL + LD A,(FL_N_6) + LD (HL),A + LD DE,RST10 + ADD HL,DE + LD (HL),0 + LD HL,(FL_N_4) + LD DE,START + LD B,9 + CALL WRITE_DISK + LD A,(L_5D23) + ADD A,9 + LD B,A + LD C,0 + LD HL,(VAR_1) + CALL AUTO_1E2E + JP AUTO_03E1 +AUTO_1775: + CALL INPUT_TXT + CALL SINTAX_RET + CALL AUTO_1C57 + CALL AUTO_03FD + LD HL,FL_NAME + LD DE,DISK_NAME + LD BC,RST08 + LDIR + CALL AUTO_1E43 + JP AUTO_03E1 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 + RST R38 +AUTO_17A5: + LD A,(AUTO_5CD3) + OR A + RET Z + PUSH HL + LD HL,L_5D23 + SUB (HL) + POP HL + JR NC,AUTO_17DD + LD A,(AUTO_5CD3) + LD B,A + XOR A + LD (AUTO_5CD3),A +AUTO_17BA: + PUSH BC + LD HL,(VAR_1) + PUSH HL + LD DE,(AUTO_5CD5) + CALL READ_DISK + LD HL,(CONT_SEC) + LD (AUTO_5CD5),HL + POP HL + POP BC + LD DE,(MED_START) + CALL WRITE_DISK + LD HL,(CONT_SEC) + LD (MED_START),HL + JR AUTO_17A5 +AUTO_17DD: + LD (AUTO_5CD3),A + PUSH HL + LD HL,L_5D23 + LD B,(HL) + POP HL + XOR A + JR AUTO_17BA +AUTO_17E9: + PUSH AF + LD HL,CAT_SEC + LD (HL),0 + LD A,C +AUTO_17F0: + SUB 16 + JR C,AUTO_17F7 + INC (HL) + JR AUTO_17F0 +AUTO_17F7: + ADD A,16 + LD C,A + PUSH BC + CALL AUTO_03EC + POP BC + POP AF + CALL AUTO_1CA4 + LD DE,FL_NAME + LD BC,RST10 + OR A + JR Z,AUTO_180D + EX DE,HL +AUTO_180D: + LDIR + RET + DISPLAY "6k" +; +; +; +CMD_VERIFY: + LD A,255 + LD (DISK_2_FLG),A +; +; +; +CMD_LOAD: + CALL AUTO_1852 +AUTO_1818: + CALL AUTO_1836 + CALL SINTAX_RET + LD A,255 + LD (ERR_3D00),A + LD A,(DISK_2_FLG) + CP 255 + JP Z,AUTO_03E1 + LD A,(FL_TYPE) + CP 'B' + JP Z,AUTO_012A + JP AUTO_03E1 +AUTO_1836: + CALL AUTO_187A + CALL SINTAX_RET + CALL AUTO_18AB + JP AUTO_1921 +AUTO_1842: + LD HL,(CH_ADR) + INC HL + LD A,(HL) + CP 13 + RET Z + LD A,1 + LD (DOS_ERROR),A + CALL AUTO_1DEB +AUTO_1852: + XOR A + LD (ERR_3D00),A + RET +AUTO_1857: + CALL AUTO_102E + LD A,'B' + CP B + JR NZ,AUTO_1866 + LD HL,(CH_ADR) + DEC HL + LD (CH_ADR),HL +AUTO_1866: + CALL AUTO_1DEB + LD HL,(DOS_CH_ADR) + LD (MED_START),HL + LD HL,(MED_LEN) + LD (DOS_CH_ADR),HL + XOR A + LD (DOS_ERROR),A + RET +AUTO_187A: + CALL TEST_CR + JP Z,AUTO_027B + CALL INPUT_TXT + CALL AUTO_0572 + CALL NZ,AUTO_1857 + CALL AUTO_1D8C + CP 175 + CALL Z,AUTO_1842 + CP 228 + PUSH AF + CALL AUTO_0572 + CALL Z,AUTO_102E + POP AF + CALL Z,AUTO_1BFC + CALL SINTAX_RET + CALL AUTO_292F +AUTO_18A4: + JP NZ,AUTO_03D9 + CALL AUTO_165D + RET +AUTO_18AB: + CALL TYPE_FL_X00 + OR A + LD HL,(FL_START) + JR Z,AUTO_18B7 + LD HL,(DOS_CH_ADR) +AUTO_18B7: + LD DE,(FL_PLACE) + CP 3 + LD A,(FL_SIZE) + PUSH DE + LD DE,(FL_LEN) + JR NZ,AUTO_18CB + LD DE,(MED_LEN) +AUTO_18CB: + LD B,A + LD (MED_LEN),DE + LD A,(FL_TYPE) + CP 'C' + LD A,B + JR NZ,AUTO_18FD + LD A,B + CP D + JR Z,AUTO_18F6 + DEC A + CP D + LD A,B + JR Z,AUTO_18F6 + LD A,(DOS_ERROR) + CP 3 + LD A,B + JR Z,AUTO_18F6 + XOR A + LD (DOS_ERROR),A + LD D,B + LD E,0 + LD (MED_LEN),DE + JR AUTO_18F9 +AUTO_18F6: + CALL AUTO_191B +AUTO_18F9: + LD A,B + CALL AUTO_1EAC +AUTO_18FD: + LD B,A + LD A,(FL_TYPE) + CP 'C' + POP DE + RET Z + PUSH DE + CP 'B' + PUSH AF + CALL Z,AUTO_1A01 + POP AF + CP 'D' + CALL Z,AUTO_1A4C + CALL AUTO_191B + LD A,(MED_LEN+1) + LD B,A + POP DE + RET +AUTO_191B: + LD A,3 + LD (DOS_ERROR),A + RET +AUTO_1921: + CALL AUTO_0572 + JR Z,AUTO_192D + PUSH AF + CALL AUTO_1A94 + POP AF + CP 255 +AUTO_192D: + PUSH AF + CALL Z,AUTO_195C + POP AF + JR Z,AUTO_1937 + JP WRITE_DISK +AUTO_1937: + LD A,(DOS_ERROR) + CP 3 + CALL Z,AUTO_1946 + LD HL,(E_LINE) + DEC HL + LD (HL),128 + RET +AUTO_1946: + LD A,(DISK_2_FLG) + CP 255 + JP NZ,AUTO_2FDE + LD A,(MED_LEN) + OR A + RET Z + LD C,A + LD B,1 + LD DE,(CONT_SEC) + JR AUTO_196A +AUTO_195C: + LD A,(DISK_2_FLG) + CP 255 + JP NZ,READ_DISK + LD (CONT_SEC),DE + LD C,0 +AUTO_196A: + LD A,B + OR A + RET Z + LD (MED_START),HL + LD (DOS_CH_ADR),DE +AUTO_1974: + PUSH BC + LD B,1 + LD DE,(DOS_CH_ADR) + LD HL,BUFER + CALL READ_DISK + LD HL,(CONT_SEC) + LD (DOS_CH_ADR),HL + POP BC + LD HL,(MED_START) + LD DE,BUFER +AUTO_198E: + LD A,(DE) + CP (HL) + JR NZ,AUTO_199D + INC HL + INC DE + DEC C + JR NZ,AUTO_198E + LD (MED_START),HL + DJNZ AUTO_1974 + RET +AUTO_199D: + LD HL,AUTO_276B + LD A,13 + JP AUTO_1C4A +; +; +; +CMD_PEEK: + LD A,255 + JR AUTO_19AB +; +; +; +CMD_POKE: + LD A,238 +AUTO_19AB: + LD (ERR_3D00),A + JP AUTO_1818 +; +; +; +CMD_MERGE: + LD A,255 + LD (FL_NUMBER+1),A + CALL AUTO_187A + CALL SINTAX_RET + LD A,(FL_TYPE) + CP 'B' + JP NZ,CMD_ERROR + LD BC,(FL_START) + LD (MED_LEN),BC + PUSH BC + INC BC + RST RST20 + DW 00030H +AUTO_19D1: + LD (HL),128 + EX DE,HL + POP DE + PUSH HL + LD DE,(FL_PLACE) + CALL AUTO_191B + LD A,(MED_LEN+1) + LD B,A + CALL AUTO_1852 + CALL AUTO_1921 + POP HL + LD DE,(BAS_PROG) + RST RST20 + DW 008D2H + JP AUTO_03E1 +AUTO_19F2: + EX DE,HL + SCF + SBC HL,DE + RET C + LD DE,CONT_1+1 + ADD HL,DE + LD B,H + LD C,L +AUTO_19FD: + RST RST20 + DW 01F05H + RET +AUTO_1A01: + LD DE,(BAS_PROG) + LD HL,(E_LINE) + DEC HL + PUSH HL + PUSH DE + SBC HL,DE + LD DE,(FL_START) + PUSH DE + PUSH HL + LD HL,START + LD A,(DISK_2_FLG) + CP 255 + JR Z,AUTO_1A20 + LD HL,START+5 +AUTO_1A20: + ADD HL,DE + LD (MED_LEN),HL + POP HL + LD A,(DISK_2_FLG) + CP 255 + JR NZ,AUTO_1A31 + POP DE + POP DE + POP HL + JR AUTO_1A48 +AUTO_1A31: + CALL AUTO_19F2 + POP BC + POP DE + POP HL + PUSH BC + RST RST20 + DW 019E5H + POP BC + CALL AUTO_1E32 + INC HL + LD BC,(FL_LEN) + ADD HL,BC + LD (BAS_VARS),HL +AUTO_1A48: + LD HL,(BAS_PROG) + RET +AUTO_1A4C: + LD DE,(FL_LEN) + LD (MED_LEN),DE + LD HL,(MED_START) + LD A,(DISK_2_FLG) + CP 255 + RET Z + LD HL,(DOS_CH_ADR) + PUSH HL + CALL AUTO_19F2 + POP HL + LD A,H + OR L + JR Z,AUTO_1A79 + LD HL,(MED_START) + DEC HL + DEC HL + DEC HL + LD BC,(DOS_CH_ADR) + INC BC + INC BC + INC BC + CALL AUTO_1E2E +AUTO_1A79: + LD HL,(E_LINE) + DEC HL + LD BC,(FL_LEN) + PUSH BC + INC BC + INC BC + INC BC + CALL AUTO_1E32 + INC HL + LD A,(AUTO_5CD2) + LD (HL),A + INC HL + POP DE + LD (HL),E + INC HL + LD (HL),D + INC HL + RET +AUTO_1A94: + LD A,(DOS_CH_ADR) + LD C,B + LD B,A + LD A,C + CP B + JR C,AUTO_1AB6 + LD A,B + OR A + JP Z,CMD_ERROR + DEC B + JR Z,AUTO_1AB0 + LD A,16 +AUTO_1AA7: + INC E + CP E + JR NZ,AUTO_1AAE + LD E,0 + INC D +AUTO_1AAE: + DJNZ AUTO_1AA7 +AUTO_1AB0: + LD B,1 + LD HL,(MED_START) + RET +AUTO_1AB6: + LD HL,AUTO_29ED + LD A,5 + JP AUTO_1C4A +AUTO_1ABE: + CALL AUTO_292F + CALL Z,SET_7_TYPE +AUTO_1AC4: + CALL AUTO_03FD + LD A,(N_FILES) + CP 128 + JP Z,AUTO_2723 + RET +; +; +; +CMD_SAVE: + CALL AUTO_1852 + LD HL,START + LD (AUTO_5CD1),HL + CALL INPUT_TXT + CALL AUTO_1D8C + CP 175 + JR Z,AUTO_1B39 + CP 202 + JR NZ,AUTO_1AF8 + CALL AUTO_1E0B + CALL SINTAX_RET + LD HL,(MED_LEN) + LD (AUTO_5CD1),HL + LD HL,FL_TYPE + JR AUTO_1B1F +AUTO_1AF8: + CP 170 + JR NZ,AUTO_1B0D + LD HL,AUTO_4000 + DB 34 +AUTO_1B00: + RST RST10 + LD E,H + LD HL,AUTO_1B00 + LD (DOS_CH_ADR),HL + LD (MED_LEN),HL + JR AUTO_1B48 +AUTO_1B0D: + CALL SINTAX_RET + CALL AUTO_1D8C + LD HL,FL_TYPE + CP 228 + JR Z,AUTO_1B2C + CP 13 + JP NZ,CMD_ERROR +AUTO_1B1F: + LD (HL),'B' + CALL AUTO_1ABE + CALL AUTO_1E1C +AUTO_1B27: + CALL AUTO_1BDE + JR AUTO_1B53 +AUTO_1B2C: + LD (HL),'D' + CALL AUTO_1ABE + CALL AUTO_1C0F + JR NC,AUTO_1B53 + JP C,CMD_ERROR +AUTO_1B39: + CALL AUTO_1DE5 + LD HL,(DOS_CH_ADR) + LD (MED_START),HL + LD HL,(MED_LEN) + LD (DOS_CH_ADR),HL +AUTO_1B48: + CALL SINTAX_RET + LD A,'C' + LD (FL_TYPE),A + CALL AUTO_1ABE +AUTO_1B53: + CALL OWERWR_X01 + JP AUTO_0569 +AUTO_1B59: + LD HL,(MED_START) + LD (FL_START),HL + EX DE,HL + LD HL,(DOS_CH_ADR) + LD A,L + OR H + JP Z,CMD_ERROR + LD A,L + OR A + JR Z,AUTO_1B6D + INC H +AUTO_1B6D: + LD A,H + LD (FL_SIZE),A + LD E,A + LD D,0 + DB 42,10 +AUTO_1B76: + LD E,(HL) + SBC HL,DE + JP C,AUTO_1C45 + PUSH HL + LD HL,(E_LINE) + LD (HL),170 + INC HL + LD DE,(AUTO_5CD1) + LD (HL),E + INC HL + LD (HL),D + LD HL,(MED_LEN) + LD (FL_LEN),HL + LD HL,(CLEAR_SEC) + LD (FL_PLACE),HL + EX DE,HL + LD HL,(FL_START) + LD A,(FL_SIZE) + LD B,A + CALL WRITE_DISK + LD HL,(CONT_SEC) + PUSH HL + CALL AUTO_03FD + POP HL + LD (CLEAR_SEC),HL + POP HL + LD (FREE_SEC),HL + LD HL,N_FILES + LD A,(HL) + LD (FL_NUMBER),A + INC (HL) + PUSH HL + CALL AUTO_1E43 + POP HL + LD C,(HL) + DEC C +AUTO_1BBF: + LD A,(FL_TYPE) + CP 'B' + CALL Z,AUTO_1BC8 + RET +AUTO_1BC8: + LD HL,(E_LINE) + LD DE,(BAS_PROG) + SCF + SBC HL,DE + LD (FL_START),HL + LD HL,(BAS_VARS) + SBC HL,DE + LD (FL_LEN),HL + RET +AUTO_1BDE: + LD HL,(BAS_VARS) + LD DE,(BAS_PROG) + SBC HL,DE + LD (MED_LEN),HL + LD HL,(BAS_PROG) + LD (MED_START),HL + LD HL,(E_LINE) + INC HL + INC HL + INC HL + SBC HL,DE + LD (DOS_CH_ADR),HL + RET +AUTO_1BFC: + CALL AUTO_1C1B + RET NC + LD HL,START + LD (DOS_CH_ADR),HL + LD A,(DISK_2_FLG) + CP 255 + RET NZ + JP AUTO_1C13 +AUTO_1C0F: + CALL AUTO_1C1B + RET NC +AUTO_1C13: + LD A,14 + LD HL,AUTO_27DD + JP AUTO_1C4A +AUTO_1C1B: + CALL BASIC_RST20 + CALL AUTO_1D9B + SET 7,C + LD A,C + LD (AUTO_5CD2),A + JR NC,AUTO_1C2B +AUTO_1C29: + SCF + RET +AUTO_1C2B: + JR NZ,AUTO_1C29 + INC HL + LD E,(HL) + INC HL + LD D,(HL) + INC HL + LD (MED_START),HL + LD (MED_LEN),DE + LD (DOS_CH_ADR),DE + CALL BASIC_RST20 + CP ')' + JR NZ,AUTO_1C2B + RET +AUTO_1C45: + LD HL,AUTO_29BB + LD A,3 +AUTO_1C4A: + CALL AUTO_03C3 + JP AUTO_01D3 +AUTO_1C50: + LD HL,AUTO_29C5 + LD A,2 + JR AUTO_1C4A +AUTO_1C57: + LD HL,FL_NAME + LD B,8 +AUTO_1C5C: + LD (HL),' ' + INC HL + DJNZ AUTO_1C5C + CALL AUTO_0531 + EX DE,HL + CALL SET_DRIVE + LD A,C + OR A + JP Z,CMD_ERROR + CP 9 + JR C,AUTO_1C73 + LD C,8 +AUTO_1C73: + LD A,(HL) + CP ' ' + JP C,CMD_ERROR + LD DE,FL_NAME + PUSH BC + LDIR + POP BC + RET +SET_DRIVE: + INC HL + LD A,(HL) + CP ':' + JR NZ,AUTO_1C98 + DEC HL + LD A,(HL) + CALL GET_DISK + + PUSH BC + PUSH HL + CALL SET_DISK + POP HL + POP BC + + DEC BC + DEC BC + INC HL + INC HL + RET +AUTO_1C98: + DEC HL + LD A,(CONT_DISK) + + PUSH BC + PUSH HL + CALL SET_DISK + POP HL + POP BC + RET +AUTO_1CA4: + LD L,C + LD H,0 + ADD HL,HL + ADD HL,HL + ADD HL,HL + ADD HL,HL + LD BC,BUFER + ADD HL,BC + RET +AUTO_1CB0: + CALL AUTO_1C57 +AUTO_1CB3: + CALL AUTO_03E8 + LD B,128 + LD C,0 +AUTO_1CBA: + PUSH BC + CALL AUTO_1CA4 + CALL AUTO_050C + POP BC + PUSH BC + LD A,C + CP 16 + JR NZ,AUTO_1CCD + POP BC + LD C,0 + JR AUTO_1CBA +AUTO_1CCD: + LD DE,FL_NAME + LD A,(S_NAME_NUM) + LD B,A + XOR A + CP (HL) + JR NZ,AUTO_1CDB + POP BC + JR AUTO_1CE4 +AUTO_1CDB: + CALL AUTO_2713 + POP BC + JR Z,AUTO_1CE7 + INC C + DJNZ AUTO_1CBA +AUTO_1CE4: + OR 255 + RET +AUTO_1CE7: + LD A,128 + SUB B + LD C,A + LD (FL_NUMBER),A + XOR A + RET Z +AUTO_1CF0: + CALL AUTO_1CB3 + LD HL,DOS_ERR_2 + LD (HL),C + RET Z + LD (HL),255 + RET +; +; +; +CMD_RETURN: + CALL SINTAX_RET + CALL AUTO_20E5 + RES 3,(IY+1) + CALL AUTO_0232 + LD SP,(DOS_SP) + LD HL,(ERR_SP) + DEC HL + LD A,18 + CP (HL) + JR NZ,AUTO_1D19 + DEC HL + LD (ERR_SP),HL +AUTO_1D19: + RET +CMD_ERROR: + BIT 7,(IY+0) + JR Z,AUTO_1D25 + LD A,11 + LD (ERR_NR),A +AUTO_1D25: + INC A + LD HL,AUTO_29B2 +ERR_EXIT: + CALL AUTO_03C3 + JP AUTO_01D3 +AUTO_1D2F: + LD A,(ERR_NR) + LD HL,AUTO_27CA + CP 20 + JR Z,ERR_EXIT + CP 12 + JR Z,ERR_EXIT + LD HL,AUTO_27D2 + CP 3 + JR Z,ERR_EXIT + LD HL,AUTO_27DD + CP 1 + JR Z,ERR_EXIT + JR CMD_ERROR +; +; +; +CMD_RUN: + CALL AUTO_1852 + CALL AUTO_1836 + CALL SINTAX_RET + LD HL,(FL_START) + LD A,(FL_TYPE) + CP 'B' + JP Z,AUTO_012A + PUSH HL + RET +AUTO_1D63: + LD HL,BAS_DOS_FLG + LD A,(HL) + DB 254 +AUTO_1D68: + RST R38 + LD (HL),0 + RET NZ + RST RST20 + DW 016BFH + RET +AUTO_1D70: + BIT 7,(IY+1) + RET +SINTAX_RET: + CALL AUTO_1D70 + RET NZ + POP HL + RET +AUTO_1D7B: + CALL BASIC_RST20 + CALL AUTO_1DC1 + JR AUTO_1D70 +OPEN_CH0: + XOR A +AUTO_1D84: + RST RST20 + DW 01601H + RET +OPEN_CH2: + LD A,2 + JR AUTO_1D84 +AUTO_1D8C: + RST RST20 + DW 00018H +AUTO_1D8F: + RET + CALL OPEN_CH0 + RST RST20 + DW 00F2CH + RET +AUTO_1D97: + RST RST20 + DW 00D6BH + RET +AUTO_1D9B: + RST RST20 + DW 028B2H + RET +AUTO_1D9F: + RST RST20 + DW 00D6EH + RET +AUTO_1DA3: + LD C,A +AUTO_1DA4: + LD B,0 + JP AUTO_1DA9 +AUTO_1DA9: + PUSH BC + CALL xchg_intf1 + POP BC + RST RST20 + DW 01A1BH + CALL xchg_intf1 + RET +BAS_2BF1: + RST RST20 + DW 02BF1H + RET +AUTO_1DB9: + RST RST20 + DW 01E99H + RET +AUTO_1DBD: + RST RST20 + DW 01C8CH + RET +AUTO_1DC1: + RST RST20 + DW 01C82H + RET +AUTO_1DC5: + LD HL,(ADR_DOS_COM) +AUTO_1DC8: + INC HL + LD (CH_ADR),HL + RET +AUTO_1DCD: + CALL INPUT_TXT +AUTO_1DD0: + CALL AUTO_1D8C + CP ',' + JP NZ,CMD_ERROR + CALL BASIC_RST20 + CALL AUTO_1DBD + RET +INPUT_TXT: + CALL AUTO_1DC5 + JP AUTO_1DBD +AUTO_1DE5: + CALL AUTO_1D8C + CP 175 + RET NZ +AUTO_1DEB: + CALL AUTO_1D7B + JR Z,AUTO_1DFB + CALL AUTO_1DB9 + LD (DOS_CH_ADR),BC + LD (MED_LEN),BC +AUTO_1DFB: + CALL AUTO_1D8C + CP ',' + JR Z,AUTO_1E0B + CP 13 + JP NZ,CMD_ERROR + CALL SINTAX_RET + RET +AUTO_1E0B: + CALL AUTO_1D7B + RET Z + CALL AUTO_1DB9 + LD (MED_LEN),BC + LD A,3 + LD (DOS_ERROR),A + RET +AUTO_1E1C: + LD HL,(ADR_DOS_COM) + RST RST20 + DW 011A7H + RET +AUTO_1E23: + LD HL,(WORK_SP) + RST RST20 + DW 00030H +AUTO_1E29: + RET +BASIC_RST20: + RST RST20 + DW 00020H +AUTO_1E2D: + RET +AUTO_1E2E: + RST RST20 + DW 019E8H + RET +AUTO_1E32: + RST RST20 + DW 01655H + RET +READ_TRK_NUM: + CALL READ_ADR_LABL + LD A,H + OUT (WG_TRK),A + RET +READ_DISK: + XOR A + JR AUTO_1E64 +AUTO_1E40: + CALL AUTO_166B +AUTO_1E43: + LD DE,(CONT_SEC) + DEC DE + LD B,1 + LD HL,BUFER +WRITE_DISK: + PUSH HL + PUSH DE + CALL get_disk_type + BIT 7,(HL) + JR Z,AUTO_1E60 + BIT 0,(HL) + JR NZ,AUTO_1E60 + LD HL,READ_ONLY_MSG + JP ERR_EXIT +AUTO_1E60: + POP DE + POP HL +AUTO_1E62: + LD A,255 +AUTO_1E64: + LD (RD_WR_COM),A +AUTO_1E67: + LD (CONT_SEC),DE + JP R_W_REDIR + + LD (HL),30 ; НЕ исп. !!! + +AUTO_1E70: + POP HL + POP BC + XOR A + OR B + RET Z +AUTO_1E75: + PUSH BC + PUSH HL + CALL save_bufer + LD A,(CONT_SEC) + CALL save_sector + LD A,(CONT_TRK) + CALL Global_trk + LD A,(RD_WR_COM) + OR A + PUSH AF + CALL Z,AUTO_3F0E + POP AF + CALL NZ,AUTO_3F0A + POP HL + LD DE,AUTO_0100 + ADD HL,DE + PUSH HL + LD A,16 + LD HL,CONT_SEC + INC (HL) + CP (HL) + JR NZ,AUTO_1EA7 + LD (HL),0 + LD HL,CONT_TRK + INC (HL) +AUTO_1EA7: + POP HL + POP BC + DJNZ AUTO_1E75 + RET +AUTO_1EAC: + PUSH HL + LD H,A + LD L,0 + PUSH HL + SBC HL,DE + CALL C,AUTO_1EBC + POP HL + LD A,H + POP HL + RET C + LD A,D + RET +AUTO_1EBC: + XOR A + LD (DOS_ERROR),A + SCF + RET +; +; +; +CMD_FORMAT: + LD HL,START + LD (MED_START),HL + LD (DOS_CH_ADR),HL + LD (AUTO_5CD1),HL + CALL TEST_CR + JP Z,CMD_ERROR ; FORMATER ; !!!!! ( CMD_ERROR ) + CALL FORM_STATE ; 1/2/3 INPUT_TXT + CALL SINTAX_RET + CALL FORM_X03 + JP RAMD_X14 +AUTO_1EE0: + AND 128 + LD A,'(' + JR Z,AUTO_1EE8 + LD A,'P' +AUTO_1EE8: + LD (MED_START),A + CALL AUTO_3D98 + CALL set_side_1 + CALL pause_750ms + LD E,1 + CALL AUTO_1FFD + CALL set_side_0 + LD E,0 + CALL AUTO_1FFD + LD A,(FL_NAME) + CP '$' + JR Z,AUTO_1F1B + CALL set_side_1 + CALL pause_750ms + CALL READ_ADR_LABL2 + LD A,H + CP 1 + JR NZ,AUTO_1F1B + LD A,128 + LD (DOS_CH_ADR+1),A +AUTO_1F1B: + CALL FORM_X02 ; форматирование !!! + LD HL,BUFER + LD (HL),0 + LD DE,BUFER+1 + LD BC,0FFh + LDIR + LD BC,MED_START + LD DE,DOS_CH_ADR+1 + LD A,(BC) + CP 'P' + JR Z,AUTO_1F49 + LD A,(DE) + CP 128 + JR Z,AUTO_1F42 + LD A,25 + LD HL,0270H + JR AUTO_1F55 +AUTO_1F42: + LD A,23 +AUTO_1F44: + LD HL,04F0h + JR AUTO_1F55 +AUTO_1F49: + LD A,(DE) + CP 128 + LD A,24 + JR NZ,AUTO_1F44 + LD A,22 + LD HL,09F0h +AUTO_1F55: + LD (TYPE_DISK),A + LD (FREE_SEC),HL + LD A,1 + LD (CLEAR_TRK),A + LD A,16 + LD (CODE_10H),A + LD HL,DISK_MRK_1 + LD DE,DISK_ALT_NM + LD BC,RST08 + LD (HL),' ' + LDIR + LD HL,FL_NAME + LD DE,DISK_NAME + LD BC,RST08 + LDIR + CALL set_side_0 + LD B,1 + LD DE,8 + LD HL,BUFER + CALL AUTO_1E62 + LD A,(DOS_ERROR) + PUSH AF + XOR A + LD (FL_TYPE),A + LD HL,(FREE_SEC) + LD (MED_START),HL + LD HL,FL_NAME + RST PRINT_LN + LD A,13 + RST RST10 + LD HL,(MED_START) + POP AF + PUSH HL + LD D,0 + LD E,A + SBC HL,DE + LD B,H + LD C,L + CALL AUTO_1DA9 + LD A,'/' + RST RST10 + POP BC + CALL AUTO_1DA9 + JP AUTO_01D3 +AUTO_1FB9: + DB 1,9,2,10,3,11,4,12,5,13,6,14,7,15,8 + DB 16,1 +test_d_traks: + CALL get_disk_time + OR 17 + LD B,A + LD A,'2' + CALL Goto_trk + LD A,2 + CALL Goto_trk + CALL pause_50ms + IN A,(WG_COM) + AND 4 + LD A,'P' + JR Z,AUTO_1FE7 + LD A,'(' +AUTO_1FE7: + LD (MED_START),A + RET +set_side_0: + LD A,(PDOS_COPY) + OR '<' ; 3C - сторона 0 3C..3F +AUTO_1FF0: + LD (PDOS_COPY),A + OUT (P_DOS_FF),A + RET +set_side_1: + LD A,(PDOS_COPY) + AND 'o' ; 06Fh - сторона 1 2C..2F + JR AUTO_1FF0 ; ld a,(pdos_copy) +AUTO_1FFD: ; add a,0C0h + DI ; ld d,0 + LD A,244 ; rl d +AUTO_2000: ; + OUT (WG_COM),A ; + LD HL,(FL_START) ; + LD C,127 + + +;//////////////////////////////////// + +AUTO_2007: + LD B,3Eh ; первый пробел + LD D,'N' + CALL AUTO_20B1 +FORM_TR_LOOP: + LD B,12 + LD D,0 + CALL AUTO_20B1 + LD B,3 + LD D,245 ; синхро + CALL AUTO_20B1 + LD D,254 ; адресная метка + CALL AUTO_20AF + LD D,E ; трек + CALL AUTO_20AF + LD D,0 ; сторона + CALL SIDE_FORMAT + LD D,(HL) ; сектор + CALL AUTO_20AF + LD D,1 ; длина - 256 + CALL AUTO_20AF + LD D,247 ; контрольный код + CALL AUTO_20AF + LD B,32 ; второй пробел + LD D,'N' + CALL AUTO_20B1 + LD B,12 + LD D,0 + CALL AUTO_20B1 + LD B,3 + LD D,245 ; синхро + CALL AUTO_20B1 + LD D,251 ; метка данных + CALL AUTO_20AF + LD B,0 ; данные: все нули + LD D,0 + CALL AUTO_20B1 + LD D,247 ; контрольный код + CALL AUTO_20AF + LD B,3Ch ; третий пробел ??? + LD D,'N' + CALL AUTO_20B1 + LD A,(HL) + INC HL + CP 16 ; проверка на посл. сектор + JR NZ,FORM_TR_LOOP ; если нет, то продолжать + IN A,(WG_COM) + AND '@' + JP NZ,AUTO_3F39 + LD A,216 + OUT (WG_COM),A + LD A,208 + OUT (WG_COM),A + +;////////////////////////////////////// + + NOP + NOP + NOP +AUTO_207D: + LD A,(MED_START+1) + OR A + RET NZ + LD C,127 + LD A,E + OUT (WG_TRK),A + CALL SUB_HL_14 +AUTO_208A: + LD B,3 + LD A,(HL) + OUT (WG_SEC),A + PUSH HL +AUTO_2090: + DI + LD A,128 + OUT (WG_COM),A + PUSH BC + CALL AUTO_3FE5 + IN A,(WG_COM) + AND 127 + POP BC + JR Z,AUTO_20A6 + DJNZ AUTO_2090 + LD HL,DOS_ERROR + INC (HL) +AUTO_20A6: + POP HL + LD A,(HL) + INC HL + CP 1 + JR NZ,AUTO_208A + EI + RET +AUTO_20AF: + LD B,1 +AUTO_20B1: + IN A,(P_DOS_FF) + AND 192 + JR Z,AUTO_20B1 + RET M + OUT (C),D + DJNZ AUTO_20B1 + RET + +FORMAT_ALL: + LD HL,MED_START + LD B,(HL) + CALL FORM_F01 + LD E,255 +AUTO_20C6: + PUSH BC + INC E + LD A,E + LD B,27 + CALL PRINT_TRK + CALL set_side_0 ; SET SIDE 0 + CALL PRINT_SIDE0 ; FORMATING ! + LD A,(DOS_CH_ADR+1) + CP 128 + JR NZ,AUTO_20E1 + CALL set_side_1 ; SET SIDE 1 + CALL PRINT_SIDE1 ; FORMATING ! +AUTO_20E1: + POP BC + DJNZ AUTO_20C6 + RET + +AUTO_20E5: + PUSH AF + LD A,(DISK_1_FLG) + CP 255 + JR Z,AUTO_211C + POP AF + CALL BUFER_OFF +xchg_intf1: + PUSH AF + LD A,(BEG_ADRESS) + CP 244 + JR Z,AUTO_211C + XOR A + LD HL,INT_1_VAR + OR (HL) + LD (HL),255 + JR Z,AUTO_211C + LD A,(BUF_FLAG) + OR A + LD HL,AUTO_5CC3 + LD DE,AUTO_5D33 + JR NZ,AUTO_2111 + LD DE,AUTO_5E34 +AUTO_2111: + LD B,'-' +AUTO_2113: + LD C,(HL) + LD A,(DE) + LD (HL),A +AUTO_2116: + LD A,C + LD (DE),A + INC HL + INC DE + DJNZ AUTO_2113 +AUTO_211C: + POP AF + RET + +AUTO_211E: + LD HL,(E_LINE) + LD (HL),13 + LD (K_CUR),HL + INC HL + LD (HL),128 + RET + +AUTO_212A: + LD DE,(E_LINE) + LD HL,COM_LN_COPY + CALL AUTO_02B0 + RET + +INPUT_LINE: + LD A,(DOS_ERR_2) + OR A + PUSH AF + CALL NZ,AUTO_212A + POP AF + CALL Z,AUTO_211E + LD HL,(E_LINE) + CALL PRINT_CR + LD A,(CONT_DISK) + ADD A,'A' + RST RST10 + LD A,'>' + RST RST10 + LD (IY+0),255 + EI + JP EDIT_X00 +AUTO_2158: + CALL BASIC_RST20 + CALL AUTO_1D8C + CP ',' + JP NZ,CMD_ERROR + LD HL,(MED_LEN) + LD (DOS_CH_ADR),HL + CALL AUTO_1E0B + CALL SINTAX_RET + LD HL,(MED_LEN) + LD A,H + OR A + JP NZ,CMD_ERROR + INC HL + LD (MED_START),HL + LD HL,(DOS_CH_ADR) + LD (MED_LEN),HL + RET +; +; +; +CMD_OPEN: + LD HL,(ADR_DOS_COM) + LD (CH_ADR),HL + CALL AUTO_1E0B + CALL AUTO_1DD0 +AUTO_218E: + CALL AUTO_1D8C + CP 'A' + JR NC,AUTO_219A + CALL BASIC_RST20 + JR AUTO_218E +AUTO_219A: + CP 165 + PUSH AF + CALL Z,AUTO_2158 + POP AF + JR Z,AUTO_21AE + AND 223 + CP 'R' + JR Z,AUTO_21AE + CP 'W' + JP NZ,CMD_ERROR +AUTO_21AE: + LD (VAR_3),A + CALL SINTAX_RET + LD A,'#' + LD (FL_TYPE),A + LD A,0 + LD (FL_START),A + CALL AUTO_21DB + PUSH AF + CALL AUTO_220F + POP AF + PUSH AF + CALL NZ,AUTO_2288 + POP AF + CALL AUTO_2242 + LD HL,(ADR_DOS_COM) + LD BC,AUTO_0124 + ADD HL,BC + LD (ADR_DOS_COM),HL + JP AUTO_01D3 +AUTO_21DB: + LD A,10 + LD (S_NAME_NUM),A + CALL AUTO_1CB0 + PUSH AF + CALL RD_HEAD_DSK + POP AF + JR NZ,AUTO_2206 + LD A,(VAR_3) + CP 'R' + JR Z,AUTO_2201 +AUTO_21F1: + LD HL,FL_START + INC (HL) + CALL AUTO_1CB3 + JR Z,AUTO_21F1 + LD HL,FL_START + DEC (HL) + CALL AUTO_1CB3 +AUTO_2201: + CALL AUTO_165D + XOR A + RET +AUTO_2206: + LD A,(VAR_3) + CP 'R' + RET NZ + JP AUTO_03D9 +AUTO_220F: + LD A,(MED_LEN) + RST RST20 + DW 01727H + LD A,B + OR C + JP NZ,AUTO_221B + RET +AUTO_221B: + LD A,25 + LD (ERR_NR),A + LD HL,AUTO_2804 + LD A,10 +AUTO_2225: + JP AUTO_1C4A +AUTO_2228: + LD A,11 + LD HL,AUTO_2812 + JR AUTO_2225 +AUTO_222F: + PUSH HL + LD C,' ' + RST RST28 + LD A,(MED_START) + LD (HL),A + INC HL + XOR A + LD (HL),A + INC HL + LD (HL),A + INC HL + LD (HL),A + LD A,127 + POP HL + RET +AUTO_2242: + PUSH AF + CALL AUTO_220F + EX DE,HL + LD HL,(BAS_PROG) + LD BC,(CHANS) + SBC HL,BC + EX DE,HL + LD (HL),E + INC HL + LD (HL),D + CALL AUTO_22B2 + LD A,(VAR_3) + CP 165 + CALL Z,AUTO_222F + JR Z,AUTO_226B + LD A,(VAR_3) + CP 'R' + LD A,255 + JR NZ,AUTO_226B + XOR A +AUTO_226B: + LD (HL),A + POP AF + JP AUTO_2270 +AUTO_2270: + PUSH AF + LD BC,CONT_2+1 + ADD HL,BC + PUSH HL + CALL AUTO_2423 + POP HL + INC HL + LD B,1 + POP AF + OR A + PUSH AF + CALL NZ,WRITE_DISK + POP AF + CALL Z,READ_DISK + RET +AUTO_2288: + LD HL,(MED_START) + PUSH HL + LD HL,AUTO_2000 + LD (MED_START),HL + CALL AUTO_229A + POP HL + LD (MED_START),HL + RET +AUTO_229A: + LD HL,AUTO_1000 + LD (DOS_CH_ADR),HL + CALL AUTO_1AC4 + CALL AUTO_1B59 + LD HL,START + LD (FL_LEN),HL + CALL AUTO_166B + JP AUTO_1E43 +AUTO_22B2: + LD HL,(BAS_PROG) + DEC HL + LD (CUR_CHL),HL + PUSH HL + LD BC,AUTO_0124 + CALL AUTO_1E32 + LD A,0 + LD B,0 +AUTO_22C4: + LD (DE),A + DEC DE + DJNZ AUTO_22C4 + POP HL + PUSH HL + LD DE,AUTO_3D0E + LD (HL),E + INC HL + LD (HL),D + INC HL + LD DE,AUTO_3D06 + LD (HL),E + INC HL + LD (HL),D + INC HL + LD (HL),'D' + INC HL + INC HL + INC HL + INC HL + INC HL + LD (HL),'$' + INC HL + LD (HL),1 + INC HL + LD A,(OPER_DISK) + LD (HL),A + INC HL + LD A,(FL_NUMBER) + LD (HL),A + INC HL + LD A,(VAR_3) + CP 'R' + LD (HL),0 + JR Z,AUTO_22FC + LD A,(FL_LEN) + LD (HL),A +AUTO_22FC: + INC HL + LD (HL),B + JR Z,AUTO_2304 + LD A,(FL_LEN+1) + LD (HL),A +AUTO_2304: + INC HL + EX DE,HL + POP HL + PUSH DE + LD DE,RST10 + ADD HL,DE + EX DE,HL + LD HL,FL_NAME + LD BC,RST10 + LDIR + POP HL + RET +AUTO_2317: + LD C,13 + RST RST28 + LD C,(HL) + RST RST28 + LD BC,CONT_4 + ADD HL,BC + RET +AUTO_2321: + LD C,'$' +RST28_EXE: + LD B,0 + LD HL,(CUR_CHL) + ADD HL,BC + RET +AUTO_232A: + LD C,13 + RST RST28 + INC (HL) + RET NZ + PUSH HL + CALL AUTO_2443 + CALL AUTO_23FC + POP HL + INC HL + INC (HL) + PUSH HL + CALL AUTO_2418 + POP HL + LD A,16 + CP (HL) + RET NZ + PUSH HL + LD C,15 + RST RST28 + LD A,(HL) + CP 127 + POP HL + JR Z,AUTO_2358 + LD HL,(CUR_CHL) + CALL AUTO_26E1 + LD C,14 + RST RST28 + JP AUTO_2379 +AUTO_2358: + CALL AUTO_23CF + PUSH AF + CALL Z,AUTO_2418 + LD C,14 + RST RST28 + POP AF + CALL NZ,AUTO_2370 + RET +AUTO_2367: + LD (HL),0 + LD C,25 + RST RST28 + LD D,' ' + LD E,(HL) + RET +AUTO_2370: + CALL AUTO_2367 + LD (MED_START),DE + JR AUTO_2381 +AUTO_2379: + CALL AUTO_2367 + INC E + LD (MED_START),DE +AUTO_2381: + CALL AUTO_229A + CALL BUFER_OFF + LD C,16 + RST RST28 + EX DE,HL + LD HL,FL_NAME + LD BC,RST10 + LDIR + LD C,12 + RST RST28 + LD A,(FL_NUMBER) + LD (HL),A + RET +AUTO_239B: + LD C,13 + RST RST28 + INC (HL) + RET NZ + INC HL + INC (HL) + PUSH HL + CALL AUTO_2443 + LD C,'#' + RST RST28 + LD A,(HL) + OR A + JR Z,AUTO_23B6 + POP HL + PUSH HL + DEC (HL) + CALL AUTO_23FC + POP HL + PUSH HL + INC (HL) +AUTO_23B6: + CALL AUTO_2418 + POP HL + LD A,16 + CP (HL) + CALL Z,AUTO_23C1 + RET +AUTO_23C1: + CALL AUTO_23CF + PUSH AF + CALL BUFER_OFF + POP AF + JP NZ,AUTO_2492 + JP AUTO_2418 +AUTO_23CF: + LD (HL),0 + LD C,25 + RST RST28 + INC (HL) + LD C,16 + RST RST28 + LD DE,FL_NAME + LD BC,RST10 + LDIR + CALL AUTO_1CB3 + RET NZ +AUTO_23E4: + CALL AUTO_165D + LD C,16 + RST RST28 + EX DE,HL + LD HL,FL_NAME + LD BC,RST10 + LDIR + LD C,12 + RST RST28 + LD A,(FL_NUMBER) + LD (HL),A + XOR A + RET +AUTO_23FC: + CALL AUTO_2423 + CALL AUTO_2321 + LD B,1 + CALL WRITE_DISK + LD C,15 + RST RST28 + LD A,(HL) + CP 127 + RET Z + CALL AUTO_2321 + XOR A + LD B,A +AUTO_2413: + LD (HL),A + INC HL + DJNZ AUTO_2413 + RET +AUTO_2418: + CALL AUTO_2423 + CALL AUTO_2321 + LD B,1 + JP READ_DISK +AUTO_2423: + LD HL,(CUR_CHL) + LD BC,AUTO_001E + ADD HL,BC + LD E,(HL) + INC HL + LD D,(HL) + LD C,14 + RST RST28 + LD B,(HL) + DEC B + INC B + PUSH AF + LD A,16 + JR Z,AUTO_2441 +AUTO_2438: + INC E + CP E + JR NZ,AUTO_243F + LD E,0 + INC D +AUTO_243F: + DJNZ AUTO_2438 +AUTO_2441: + POP AF + RET +AUTO_2443: + LD C,11 + RST RST28 + LD A,(HL) + JP SET_DISK +AUTO_244A: + LD HL,RET_INS + PUSH HL + LD HL,xchg_intf1 + PUSH HL + PUSH AF + CALL xchg_intf1 + LD A,10 + LD (S_NAME_NUM),A + POP AF + CALL AUTO_24A2 + PUSH AF + CALL AUTO_2650 + JP Z,AUTO_249E + POP AF + CALL AUTO_2317 + LD (HL),A + JP AUTO_232A +AUTO_246E: + LD C,13 + RST RST28 + LD A,(HL) + LD BC,CONT_1+5 + ADD HL,BC + CP (HL) + RET NZ + LD C,14 + RST RST28 + LD A,(HL) + LD BC,CONT_1+5 + ADD HL,BC + CP (HL) + RET NZ + LD HL,BEG_ADRESS + LD A,(HL) + CP 244 + JR Z,AUTO_2492 + BIT 4,(HL) + JR Z,AUTO_2492 + OR 1 + POP HL + RET +AUTO_2492: + LD A,7 +AUTO_2494: + LD (ERR_NR),A + CALL AUTO_20E5 + RST RST20 + DW 00058H + RET +AUTO_249E: + LD A,23 + JR AUTO_2494 +AUTO_24A2: + LD D,A + LD C,15 + RST RST28 + LD A,(HL) + CP 127 + LD A,D + RET NZ + LD BC,CONT_2 + ADD HL,BC + LD A,(HL) + OR A + LD A,D + JR NZ,AUTO_24D5 + DEC HL + LD A,(HL) + OR A + JR NZ,AUTO_24C2 + PUSH BC + PUSH HL + PUSH DE + CALL AUTO_24F8 + POP DE + POP HL + POP BC +AUTO_24C2: + LD C,(HL) + LD A,D + EX DE,HL + LD HL,(VAR_1) + ADD HL,BC + CP 6 + LD (HL),A + CALL Z,AUTO_2504 + LD C,'!' + RST RST28 + INC (HL) + POP HL + RET +AUTO_24D5: + DEC HL + LD A,(HL) + DEC HL + INC A + CP (HL) + INC HL + INC (HL) + PUSH HL + PUSH AF + LD C,'#' + RST RST28 + LD (HL),255 + POP AF + POP HL + JR C,AUTO_24EE + LD A,D + CP 13 + JR Z,AUTO_24F2 + POP BC + RET +AUTO_24EE: + LD A,D + CP 13 + RET NZ +AUTO_24F2: + XOR A + LD (HL),A + INC HL + LD (HL),A + LD A,D + RET +AUTO_24F8: + LD HL,(WORK_SP) + LD (VAR_1),HL + LD BC,RST10 + JP AUTO_1E23 +AUTO_2504: + LD (HL),13 + LD HL,(CH_ADR) + LD (MED_START),HL + LD HL,(VAR_1) + LD (CH_ADR),HL + LD HL,FLAGS + RES 7,(HL) + CALL AUTO_1DC1 + LD HL,FLAGS + SET 7,(HL) + LD HL,(VAR_1) + LD (CH_ADR),HL + CALL AUTO_1DC1 + CALL AUTO_1DB9 + PUSH BC + POP DE + LD C,' ' + RST RST28 + LD B,(HL) + XOR A + LD HL,START + LD (MED_LEN),HL +AUTO_2538: + ADD HL,DE + JR NC,AUTO_2544 + PUSH HL + LD HL,(MED_LEN) + INC HL + LD (MED_LEN),HL + POP HL +AUTO_2544: + DJNZ AUTO_2538 + LD (DOS_CH_ADR),HL + LD A,(MED_LEN) + LD HL,DOS_CH_ADR+1 + RRD + AND 15 + LD (MED_LEN),A + LD HL,(MED_START) + LD (CH_ADR),HL + CALL AUTO_2568 + LD C,'!' + RST RST28 + LD A,255 + LD (HL),A + INC HL + LD (HL),A + RET +AUTO_2568: + LD C,25 + RST RST28 + LD A,(DOS_CH_ADR+1) + CP (HL) + JP NZ,AUTO_2584 + LD C,14 + RST RST28 + LD A,(MED_LEN) + CP (HL) + JP NZ,AUTO_25A7 +AUTO_257C: + LD C,13 + RST RST28 + LD A,(DOS_CH_ADR) + LD (HL),A + RET +AUTO_2584: + CALL AUTO_25CA + CALL NZ,AUTO_25C3 + LD A,(DOS_CH_ADR+1) + LD C,25 + RST RST28 + LD (HL),A + LD C,16 + RST RST28 + LD DE,FL_NAME + LD BC,RST10 + LDIR + CALL AUTO_1CB3 + JP NZ,AUTO_25D2 + CALL AUTO_23E4 + JR AUTO_25AD +AUTO_25A7: + CALL AUTO_25CA + CALL NZ,AUTO_25C3 +AUTO_25AD: + LD A,(MED_LEN) + LD C,14 + RST RST28 + LD (HL),A + PUSH HL + CALL AUTO_2443 + CALL AUTO_2418 + POP HL + DEC HL + LD A,(DOS_CH_ADR) + LD (HL),A + JR AUTO_257C +AUTO_25C3: + CALL AUTO_2443 + CALL AUTO_23FC + RET +AUTO_25CA: + LD C,'#' + RST RST28 + LD A,(HL) + OR A + LD (HL),0 + RET +AUTO_25D2: + LD HL,(DOS_CH_ADR+1) + LD H,' ' + LD (MED_START),HL + LD HL,(DOS_CH_ADR) + PUSH HL + LD HL,(MED_LEN) + PUSH HL + CALL AUTO_2381 + POP HL + LD (MED_LEN),HL + POP HL + LD (DOS_CH_ADR),HL + JR AUTO_25AD +AUTO_25EF: + CALL xchg_intf1 + LD HL,TV_FLAG + RES 3,(HL) + LD HL,(ERR_SP) + LD E,(HL) + INC HL + LD D,(HL) + OR A + LD HL,AUTO_107F + SBC HL,DE + JR NZ,AUTO_2626 + LD SP,(ERR_SP) + POP DE + POP DE + LD (ERR_SP),DE +AUTO_260F: + CALL AUTO_262B + JR C,AUTO_261D +AUTO_2614: + LD HL,RET_INS + PUSH HL + LD HL,AUTO_20E5 + PUSH HL + RET +AUTO_261D: + CP 13 + JR Z,AUTO_2614 + RST RST20 + DW 00F85H + JR AUTO_260F +AUTO_2626: + CALL AUTO_262B + JR AUTO_2614 +AUTO_262B: + LD A,10 + LD (S_NAME_NUM),A + CALL AUTO_2650 + JR Z,AUTO_2642 + CP 127 + JP NZ,AUTO_249E + LD BC,CONT_2 + ADD HL,BC + LD (HL),0 + JR AUTO_2645 +AUTO_2642: + CALL AUTO_246E +AUTO_2645: + CALL AUTO_2317 + LD A,(HL) + PUSH AF + CALL AUTO_239B + POP AF + SCF + RET +AUTO_2650: + LD C,15 + RST RST28 + LD A,(HL) + OR A + RET +; +; +; +CMD_CLOSE: + LD HL,(ADR_DOS_COM) + LD (CH_ADR),HL + CALL AUTO_1E0B + CALL SINTAX_RET + LD A,(MED_LEN) + RST RST20 + DW 01727H + LD A,B + OR C + JP Z,AUTO_01D3 + PUSH HL + LD HL,(CHANS) + ADD HL,BC + LD A,(HL) + LD HL,AUTO_3D0E + CP H + POP HL + JP NZ,AUTO_2228 + LD (HL),0 + INC HL + LD (HL),0 + LD (DOS_CH_ADR),BC + LD HL,(CHANS) + ADD HL,BC + DEC HL + LD (MED_START),HL + CALL AUTO_26CE + LD HL,(MED_START) + LD BC,AUTO_0124 + CALL AUTO_1E2E + LD HL,REP_K_TYME + LD B,16 +AUTO_269D: + PUSH BC + LD BC,(DOS_CH_ADR) + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL + SBC HL,BC + EX DE,HL + JR C,AUTO_26BC + LD D,(HL) + DEC HL + LD E,(HL) + INC HL + PUSH HL + EX DE,HL + LD BC,AUTO_0124 + SBC HL,BC + EX DE,HL + POP HL + LD (HL),D + DEC HL + LD (HL),E + INC HL +AUTO_26BC: + INC HL + POP BC + DJNZ AUTO_269D + LD HL,(ADR_DOS_COM) + LD BC,AUTO_0124 + SBC HL,BC + LD (ADR_DOS_COM),HL + JP AUTO_01D3 +AUTO_26CE: + LD BC,CONT_1+6 + ADD HL,BC + LD A,(HL) + OR A + RET Z + LD HL,(MED_START) + LD (CUR_CHL),HL + CALL AUTO_26E1 + JP AUTO_23FC +AUTO_26E1: + LD BC,CONT_1+4 + ADD HL,BC + LD E,(HL) + INC HL + LD D,(HL) + LD BC,CONT_1+4 + ADD HL,BC + LD (HL),E + INC HL + LD (HL),D + LD C,16 + RST RST28 + LD DE,FL_NAME + LD BC,RST10 + LDIR + CALL AUTO_2443 + LD C,12 + RST RST28 + LD C,(HL) + CALL AUTO_166B + JP AUTO_1E43 +PRINT_L: + LD A,(HL) + OR A + RET Z + AND 127 + RST RST10 + BIT 7,(HL) + RET NZ +AUTO_2710: + INC HL + JR PRINT_L +AUTO_2713: + LD A,(DE) + CP (HL) + RET NZ + INC DE + INC HL + DJNZ AUTO_2713 + RET +exit_No_disk: + LD HL,AUTO_27FC + LD A,6 + JP AUTO_1C4A +AUTO_2723: + LD HL,AUTO_27ED + LD A,4 + JP AUTO_1C4A +set_error_26: + LD A,26 + JR AUTO_2731 + LD A,18 +AUTO_2731: + LD (ERR_NR),A + RET + LD A,3 + JR AUTO_2731 +AUTO_2739: + XOR A + LD (MED_START+1),A + LD (DOS_ERROR),A + IN A,(WG_COM) + LD (DRV_READY),A + LD E,D + PUSH DE + LD A,E + OUT (WG_DATA),A + LD A,24 + CALL WG93_EXE + LD A,(DRV_READY) + AND 128 + CALL NZ,pause_750ms + POP DE + CALL AUTO_207D + LD A,(DOS_ERROR) + OR A + RET Z + LD A,7 + LD (DOS_ERR_2),A + RET +AUTO_2766: + DB "O.K.",0 +AUTO_276B: + DB "Verify Error.",141 +AUTO_2779: + DB "BACKUP DISK",141 +AUTO_2785: + DB "Insert Destination disk",13,"then " + DB "press Y",0 +AUTO_27AA: + DB "Insert Source disk then press Y",0 +AUTO_27CA: + DB "*BREAK*",141 +AUTO_27D2: + DB "Out of RAM",141 +AUTO_27DD: + DB "Array not found",141 +AUTO_27ED: + DB "Directory full",141 +AUTO_27FC: + DB "No disk",141 + DISPLAY "10k" +AUTO_2804: + DB "Stream opened",141 +AUTO_2812: + DB "Not disk file",141 +AUTO_2820: + DB "File exists",13,"Over write?(Y/N",169 +AUTO_283C: + PUSH AF + PUSH BC + LD (WORK_4),DE + LD (WORK_2),HL + CALL xchg_intf1 + LD A,255 + LD (MSG_FLAG),A + LD (FL_NUMBER+1),A + POP BC + POP AF + LD HL,AUTO_0201 + LD (ADR_RET),HL + LD HL,START + ADD HL,SP + LD (DOS_SP),HL + DEC HL + DEC HL + LD SP,HL + PUSH AF + CALL AUTO_021D + LD HL,AUTO_288C +AUTO_2869: + LD A,(HL) + CP C + JR NZ,AUTO_287F + POP AF + INC HL + LD E,(HL) + INC HL + LD D,(HL) + LD HL,AUTO_01D3 + PUSH HL + PUSH DE + LD HL,(WORK_2) + LD DE,(WORK_4) + RET +AUTO_287F: + CP 255 + JR NZ,AUTO_2887 + POP AF + JP AUTO_01D3 +AUTO_2887: + INC HL + INC HL + INC HL + JR AUTO_2869 +AUTO_288C: + DB 0 + DW AUTO_3D98 + DB 1 + DW SET_DISK + DB 2 + DW Global_trk + DB 3 + DW save_sector + DB 4 + DW save_bufer + DB 5 + DW READ_DISK + DB 6 + DW WRITE_DISK + DB 7 + DW AUTO_28D8 + DB 8 + DW AUTO_165C + DB 9 + DW AUTO_1664 + DB 10 + DW AUTO_1CF0 + DB 11 + DW AUTO_28FB + DB 12 + DW AUTO_28F2 + DB 13 + DW AUTO_01D3 + DB 14 + DW AUTO_290F + DB 15 + DW AUTO_01D3 + DB 16 + DW AUTO_01D3 + DB 17 + DW AUTO_01D3 + DB 18 + DW AUTO_2926 + DB 19 + DW AUTO_28E0 + DB 20 + DW AUTO_28E3 + DB 21 + DW AUTO_2739 + DB 22 + DW set_side_0 + DB 23 + DW set_side_1 + DB 24 + DW RD_HEAD_DSK + DB 255 +AUTO_28D8: + PUSH AF + CALL RD_HEAD_DSK + POP AF + JP AUTO_0479 +AUTO_28E0: + XOR A + JR AUTO_28E5 +AUTO_28E3: + LD A,255 +AUTO_28E5: + LD DE,FL_NAME + LD BC,RST10 + OR A + JR Z,AUTO_28EF + EX DE,HL +AUTO_28EF: + LDIR + RET +AUTO_28F2: + CALL RD_HEAD_DSK + CALL AUTO_1AC4 + JP AUTO_1B27 +AUTO_28FB: + LD (MED_START),HL + LD (DOS_CH_ADR),DE + LD (MED_LEN),DE + JP TYPE_FL_X02 + + CALL AUTO_1AC4 ; ??? + JP FORM_X04 ; ??? +AUTO_290F: + OR A + LD (DOS_ERROR),A + LD (DOS_CH_ADR),HL + LD (MED_LEN),DE + CALL AUTO_1CB3 + CALL AUTO_18A4 + CALL AUTO_18AB + JP AUTO_1921 +AUTO_2926: + CALL RD_HEAD_DSK + CALL AUTO_1CB3 + JP AUTO_07A0 +AUTO_292F: + CALL AUTO_1C57 + CALL RD_HEAD_DSK + JP AUTO_1CB3 +AUTO_2938: + PUSH BC + LD B,8 +AUTO_293B: + LD A,(HL) + RST RST10 + INC HL + DJNZ AUTO_293B + LD A,'<' + RST RST10 + LD A,(HL) + RST RST10 + LD A,'>' + RST RST10 + POP BC + RET +BUFER_ON: + PUSH HL + PUSH DE + PUSH BC + PUSH AF + LD HL,BUF_FLAG + LD A,(HL) + OR A + JR Z,AUTO_2992 + PUSH HL + LD BC,AUTO_0100+1 + PUSH BC + CALL AUTO_19FD + POP BC + POP HL + LD (HL),0 + LD HL,BUFER + CALL AUTO_1E32 + LD HL,(ADR_DOS_COM) + LD BC,AUTO_0100+1 + ADD HL,BC + JR AUTO_298F +BUFER_OFF: + PUSH HL + PUSH DE + PUSH BC + PUSH AF + LD HL,BUF_FLAG + LD A,(HL) + OR A + JR NZ,AUTO_2992 + LD (HL),255 + LD HL,BUFER + LD BC,AUTO_0100+1 + CALL AUTO_1E2E + OR A + LD BC,AUTO_0100+1 + LD HL,(ADR_DOS_COM) + SBC HL,BC +AUTO_298F: + LD (ADR_DOS_COM),HL +AUTO_2992: + POP AF + POP BC + POP DE + POP HL + RET +; +; +; +CMD_40: + XOR A + + +AUTO_2998: + BLOCK #2998 - $,255 + LD (MED_START),A + CALL SINTAX_RET + CALL TEST_CR + JP Z,CMD_ERROR + CALL get_disk_type + LD A,(MED_START) + LD (HL),A + JP AUTO_03E1 +; +; +; +CMD_80: + LD A,128 + JR AUTO_2998 +AUTO_29B2: + DB 13,"*ERROR*",141 +AUTO_29BB: + DB 13,"No space",141 +AUTO_29C5: + DB 13,"File exists",141 +AUTO_29D2: + DB " Free",141 +READ_ONLY_MSG: + DB 13,"Read Onl",249 +DISK_ERROR_MSG: + DB 13,"Disc Erro",242 +AUTO_29ED: + DB 13,"Rec. O/",198 +AUTO_29F7: + DB "Title:",160 +AUTO_29FE: + DB 13,"Retry,Abort,Ignore?",0 +AUTO_2A13: + DB 13,"Trk",160 +AUTO_2A18: + DB " sec",160 +AUTO_2A1D: + DB " Del. File",141 +AUTO_2A28: + DB 13,"No" +AUTO_2A2B: + DB " File(s)",141,0 +AUTO_2A35: + LD HL,AUTO_2A41 + LD DE,AUTO_4080 + LD BC,RST20 + LDIR + RET +; +; TEST 128/48 ROM программа в RAM !!! +; +AUTO_2A41: + LD A,(AUTO_03B5) + CP 243 + LD A,16 + JR Z,AUTO_2A4B + XOR A +AUTO_2A4B: + LD (K_STATE+1),A + LD BC,PORT_128 + LD A,16 + OUT (C),A + RET +MAGIC_2: +AUTO_2A56: + PUSH AF + PUSH BC + PUSH DE + PUSH HL + PUSH IX + PUSH IY + EXX + PUSH BC + PUSH DE + PUSH HL + EX AF,AF' + PUSH AF + LD A,I + PUSH AF + LD A,R + PUSH AF + LD HL,START + ADD HL,SP + PUSH HL + LD A,3Ch + OUT (P_DOS_FF),A + LD A,'?' + LD I,A + IN A,(WG_COM) + AND 128 + RRCA + RRCA + RRCA + LD (K_STATE+1),A + CALL AUTO_2F65 + CALL pause_750ms + CALL pause_750ms + LD DE,CONT_1+1 + LD HL,AUTO_4000 + PUSH HL + CALL AUTO_2D73 + LD HL,AUTO_4100 + LD DE,CONT_1+2 + CALL AUTO_2D73 + POP HL + PUSH HL + LD DE,RST08 + CALL AUTO_2F1B + LD HL,AUTO_40E3 + LD A,(HL) + LD (K_STATE),A + INC HL + LD A,(HL) + INC (HL) + INC HL + LD E,(HL) + INC HL + LD D,(HL) + OR A + EX DE,HL + LD DE,AUTO_00A8+24 + SBC HL,DE + LD (AUTO_40E5),HL + LD HL,AUTO_4000 + LD DE,RST08 + CALL AUTO_2D73 + POP HL + LD DE,(AUTO_40E1) + PUSH DE + LD DE,CONT_1+1 + CALL AUTO_2F1B + POP DE + CALL AUTO_2D4C + PUSH DE + LD A,3Ch + OUT (P_DOS_FF),A + CALL AUTO_2F65 + LD HL,AUTO_4000 + LD DE,RST08 + LD B,1 + CALL AUTO_2F1B + POP DE + LD HL,(AUTO_40E1) + LD (AUTO_40E1),DE + PUSH HL + LD HL,AUTO_4000 + LD DE,RST08 + LD B,1 + CALL AUTO_2D73 + LD A,(AUTO_40E4) + DEC A + CALL AUTO_2CE5 + LD (HL),'@' + INC HL + LD B,7 +AUTO_2B09: + LD (HL),' ' + INC HL + DJNZ AUTO_2B09 + LD (HL),'C' + POP DE + POP BC + INC HL + LD (HL),C + INC HL + LD (HL),B + INC HL + INC HL + INC HL + LD (HL),192 + INC HL + LD (HL),E + INC HL + LD (HL),D + LD HL,AUTO_4000 + LD DE,START + IN A,(WG_SEC) + DEC A + LD E,A + LD B,1 + CALL AUTO_2D73 + LD HL,START + ADD HL,SP + LD (AUTO_4140),HL + LD SP,AUTO_41FF + CALL AUTO_2A35 + LD HL,AUTO_C000 + XOR A +AUTO_2B3F: + ADD A,(HL) + INC HL + LD B,A + LD A,H + OR A + LD A,B + JR NZ,AUTO_2B3F + LD HL,AUTO_4100 + LD (HL),A + PUSH HL + LD HL,AUTO_2B58 + PUSH HL + LD HL,AUTO_3D2F + PUSH HL + DI + JP AUTO_4080 +AUTO_2B58: + POP HL + LD BC,PORT_128 + LD A,170 + LD (AUTO_4130),A + LD D,5 + LD A,(K_STATE+1) + OR D + LD D,A + OUT (C),D + LD A,(AUTO_C130) + CP 170 + JP NZ,AUTO_2C1B + LD A,D + AND 248 + LD D,A + INC HL + LD B,8 +AUTO_2B79: + LD (HL),D + OUT (C),D + XOR A + LD HL,AUTO_C000 +AUTO_2B80: + ADD A,(HL) + INC HL + LD E,A + LD A,H + OR A + LD A,E + JR NZ,AUTO_2B80 + LD HL,AUTO_4100 + CP (HL) + INC HL + JR Z,AUTO_2B93 + INC D + DJNZ AUTO_2B79 + DEC D +AUTO_2B93: + LD B,8 +AUTO_2B95: + PUSH BC + CALL AUTO_2C37 + POP BC + DJNZ AUTO_2B95 + LD C,0 + CALL AUTO_2F3A + CALL AUTO_2D2A + LD A,(AUTO_40E4) + LD (AUTO_4102),A + INC A + LD (AUTO_40E4),A + LD HL,(AUTO_40E5) + LD DE,START+1 + SBC HL,DE + LD (AUTO_40E5),HL + RET C + LD HL,(AUTO_40E1) + LD (AUTO_411E),HL + CALL AUTO_2D1E + LD A,'8' + LD (AUTO_4111),A + LD A,1 + LD (AUTO_411D),A + LD HL,AUTO_4100 + LD (AUTO_4119),HL + LD HL,AUTO_0100 + LD (AUTO_411B),HL + LD DE,(AUTO_40E1) + CALL AUTO_2F65 + LD C,D + CALL AUTO_2F3A + LD HL,AUTO_4100 + LD B,1 + CALL AUTO_2D58 + LD (AUTO_40E1),DE + LD C,0 + CALL AUTO_2F3A + CALL AUTO_2D34 + LD A,(AUTO_4102) + CALL AUTO_2CE5 + LD DE,AUTO_4110 + LD BC,RST10 + EX DE,HL + LDIR + IN A,(WG_SEC) + DEC A + LD E,A + LD D,0 + LD HL,AUTO_4000 + CALL AUTO_2D73 + LD BC,PORT_128 + LD A,(AUTO_4101) + OUT (C),A +AUTO_2C1B: + LD HL,(AUTO_4140) + LD SP,HL + LD HL,AUTO_4000 + LD DE,CONT_1+1 + CALL AUTO_2F1B + LD HL,AUTO_4100 + LD DE,CONT_1+2 + CALL AUTO_2F1B + LD A,'<' + PUSH AF + JP AUTO_2EBC +AUTO_2C37: + LD A,B + DEC A + LD (AUTO_4103),A + LD B,A + LD A,(AUTO_4101) + AND 7 + CP B + RET Z + LD A,2 + CP B + RET Z + LD A,(AUTO_4101) + AND 8 + JR Z,AUTO_2C55 + LD A,B + CP 7 + RET Z + JR AUTO_2C59 +AUTO_2C55: + LD A,B + CP 5 + RET Z +AUTO_2C59: + CALL AUTO_2C5D + RET +AUTO_2C5D: + LD HL,AUTO_4101 + LD A,(HL) + AND 248 + LD C,A + LD A,B + OR C + PUSH BC + LD BC,PORT_128 + OUT (C),A + POP BC + LD HL,AUTO_C000 +AUTO_2C70: + LD A,(HL) + OR A + JR NZ,AUTO_2C7A + INC HL + LD A,H + OR A + JR NZ,AUTO_2C70 + RET +AUTO_2C7A: + CALL AUTO_2C7E + RET +AUTO_2C7E: + LD C,0 + CALL AUTO_2F3A + CALL AUTO_2D2A + LD A,(AUTO_40E4) + LD (AUTO_4102),A + INC A + LD (AUTO_40E4),A + LD HL,(AUTO_40E5) + LD DE,CONT_6+6 + SBC HL,DE + LD (AUTO_40E5),HL + RET C + LD HL,(AUTO_40E1) + LD (AUTO_411E),HL + CALL AUTO_2D1E + LD A,'@' + LD (AUTO_411D),A + LD HL,AUTO_C000 + LD (AUTO_4119),HL + LD HL,AUTO_4000 + LD (AUTO_411B),HL + LD DE,(AUTO_40E1) + CALL AUTO_2D3E + LD (AUTO_40E1),DE + LD C,0 + CALL AUTO_2F3A + CALL AUTO_2D34 + LD A,(AUTO_4102) + CALL AUTO_2CE5 + LD DE,AUTO_4110 + LD BC,RST10 + EX DE,HL + LDIR + IN A,(WG_SEC) + DEC A + LD E,A + LD D,0 + LD HL,AUTO_4000 + CALL AUTO_2D73 + RET +AUTO_2CE5: + LD C,A + AND 240 + RRCA + RRCA + RRCA + RRCA + LD B,A + PUSH BC + LD E,B + LD D,0 + LD HL,AUTO_4000 + PUSH DE + CALL AUTO_2F1B + POP DE + POP BC + LD B,0 + LD A,C + AND 15 + RLCA + RLCA + RLCA + RLCA + LD HL,AUTO_4000 + ADD A,L + LD L,A + RET +AUTO_2D09: + LD HL,AUTO_4110 + LD B,9 +AUTO_2D0E: + LD (HL),' ' + INC HL + DJNZ AUTO_2D0E + LD A,'@' + LD (AUTO_4110),A + LD A,'C' + LD (AUTO_4118),A + RET +AUTO_2D1E: + CALL AUTO_2D09 + LD A,(AUTO_4103) + ADD A,'0' + LD (AUTO_4111),A + RET +AUTO_2D2A: + LD HL,AUTO_4000 + LD DE,RST08 + CALL AUTO_2F1B + RET +AUTO_2D34: + LD HL,AUTO_4000 + LD DE,RST08 + CALL AUTO_2D73 + RET +AUTO_2D3E: + CALL AUTO_2F65 + LD C,D + CALL AUTO_2F3A + LD HL,AUTO_C000 + LD B,'@' + JR AUTO_2D58 +AUTO_2D4C: + CALL AUTO_2F65 + LD C,D + CALL AUTO_2F3A + LD HL,AUTO_4000 + LD B,192 +AUTO_2D58: + PUSH BC + PUSH DE + CALL AUTO_2D73 + LD DE,AUTO_0100 + ADD HL,DE + POP DE + INC E + LD A,E + CP 16 + JR NZ,AUTO_2D6F + LD E,0 + INC D + LD C,D + CALL AUTO_2F3A +AUTO_2D6F: + POP BC + DJNZ AUTO_2D58 + RET +AUTO_2D73: + LD A,E + INC A + OUT (WG_SEC),A + PUSH HL + LD D,20 + PUSH DE +AUTO_2D7B: + DI + LD C,127 + LD A,160 + OUT (WG_COM),A + CALL AUTO_3FCA + POP DE + POP HL + IN A,(WG_COM) + AND 127 + RET Z + DEC D + PUSH HL + PUSH DE + JR NZ,AUTO_2D7B + HALT +AUTO_2D92: + LD HL,(FL_START) + LD DE,(FL_PLACE) + LD A,(FL_SIZE) + LD B,A + CALL READ_DISK + RET +; +; +; +CMD_GOTO: + CALL INPUT_TXT + CALL SINTAX_RET + CALL AUTO_102E + CALL AUTO_1C57 + LD (AUTO_4020),BC + LD A,C + CP 8 + JR NC,AUTO_2DD2 + CALL RD_HEAD_DSK + CALL AUTO_1CB3 + JP NZ,AUTO_03D9 + LD HL,FL_NAME + LD BC,(AUTO_4020) + ADD HL,BC + LD (HL),'8' + PUSH HL + CALL AUTO_1CB3 + POP HL + JR Z,AUTO_2DD8 + LD (HL),' ' +AUTO_2DD2: + CALL AUTO_1CB3 + JP AUTO_2E33 +AUTO_2DD8: + CALL AUTO_162A + CALL AUTO_2D92 + LD SP,AUTO_40FF + LD B,8 +AUTO_2DE3: + PUSH BC + LD A,B + LD BC,PORT_128 + DEC A + PUSH AF + OR 16 + OUT (C),A + POP AF + ADD A,'0' + LD HL,FL_NAME + LD BC,(AUTO_4020) + ADD HL,BC + LD (HL),A + CALL AUTO_1CB3 + JR NZ,AUTO_2E05 + CALL AUTO_165D + CALL AUTO_2D92 +AUTO_2E05: + POP BC + DJNZ AUTO_2DE3 + LD A,' ' + LD HL,FL_NAME + LD BC,(AUTO_4020) + ADD HL,BC + LD (HL),A + CALL AUTO_1CB3 + CALL AUTO_165D + PUSH BC + PUSH AF + LD BC,PORT_128 + LD A,(AUTO_4101) + OUT (C),A + POP AF + POP BC + JR AUTO_2E39 + CALL INPUT_TXT + CALL SINTAX_RET + CALL AUTO_102E + CALL AUTO_292F +AUTO_2E33: + JP NZ,AUTO_03D9 + CALL AUTO_165D +AUTO_2E39: + LD A,(FL_NAME) + CP '$' + DI + JR NZ,AUTO_2E43 + IM 2 +AUTO_2E43: + LD SP,AUTO_40F0 + CALL get_disk_type + LD (AUTO_4010),A + LD A,(PDOS_COPY) + LD (AUTO_4011),A + LD HL,(FL_START) + PUSH HL + LD DE,(FL_PLACE) + PUSH DE + INC E + LD A,E + CP 16 + JR NZ,AUTO_2E64 + LD E,0 + INC D +AUTO_2E64: + LD C,D + CALL AUTO_2F07 + LD A,(AUTO_4010) + AND 2 + CALL NZ,AUTO_2F0F + LD A,C + CALL AUTO_2F50 + LD HL,AUTO_4100 + LD B,191 +AUTO_2E79: + PUSH BC + PUSH DE + CALL AUTO_2F1B + LD DE,AUTO_0100 + ADD HL,DE + POP DE + INC E + LD A,E + CP 16 + JR NZ,AUTO_2E9C + LD E,0 + INC D + LD C,D + CALL AUTO_2F07 + LD A,(AUTO_4010) + AND 2 + CALL NZ,AUTO_2F0F + LD A,C + CALL AUTO_2F50 +AUTO_2E9C: + POP BC + DJNZ AUTO_2E79 + POP DE + POP HL + LD SP,HL + LD A,(AUTO_4011) + PUSH AF + LD C,D + CALL AUTO_2F07 + LD A,(AUTO_4010) + AND 2 + CALL NZ,AUTO_2F0F + LD A,C + CALL AUTO_2F50 + LD HL,AUTO_4000 + CALL AUTO_2F1B +AUTO_2EBC: + POP AF + EX AF,AF' + POP AF + LD R,A + POP AF + LD I,A + DI + LD A,255 + JP PO,AUTO_2ECC + LD A,0 +AUTO_2ECC: + LD (K_STATE),A + POP AF + POP HL + POP DE + POP BC + EXX + EX AF,AF' + POP IY + POP IX + POP HL + POP DE + POP BC + LD A,(BORDER) + AND '8' + RRCA + RRCA + RRCA + OUT (0FEH),A + LD A,(AUTO_5B08) + CP 238 + JR NZ,AUTO_2EF7 + PUSH BC + LD BC,PORT_128 + LD A,(AUTO_5B5C) + OUT (C),A + POP BC +AUTO_2EF7: + LD A,(K_STATE) + OR A + LD A,201 + LD (K_STATE),A + JR NZ,AUTO_2F03 + EI +AUTO_2F03: + POP AF + JP K_STATE +AUTO_2F07: + LD A,(AUTO_4011) + OR 3Ch +AUTO_2F0C: + OUT (P_DOS_FF),A + RET +AUTO_2F0F: + LD A,C + OR A + RRA + LD C,A + RET NC + LD A,(AUTO_4011) + AND 'o' + JR AUTO_2F0C +AUTO_2F1B: + LD A,E + INC A + OUT (WG_SEC),A + PUSH HL + LD D,20 + PUSH DE +AUTO_2F23: + DI + LD C,127 + LD A,128 + OUT (WG_COM),A + CALL AUTO_3FE5 + POP DE + POP HL + IN A,(WG_COM) + AND 127 + RET Z + DEC D + PUSH HL + PUSH DE + JR NZ,AUTO_2F23 + HALT +AUTO_2F3A: + LD A,3Ch + OUT (P_DOS_FF),A + LD A,(K_STATE) + AND 8 + JR NZ,AUTO_2F4F + LD A,C + OR A + RRA + LD C,A + JR NC,AUTO_2F4F + LD A,2Ch + OUT (P_DOS_FF),A +AUTO_2F4F: + LD A,C +AUTO_2F50: + OUT (WG_DATA),A + CALL pause_50ms + LD A,24 +AUTO_2F57: + OUT (WG_COM),A +AUTO_2F59: + IN A,(P_DOS_FF) + AND 128 + JR Z,AUTO_2F59 + PUSH BC + CALL pause_50ms + POP BC + RET +AUTO_2F65: + LD A,8 + JR AUTO_2F57 +AUTO_2F69: + LD HL,(DOS_SP) + DEC HL + DEC HL + LD SP,HL + JP AUTO_1D2F +; +; RST20 procedure +; +RST20_EXE: + LD (WORK_2),HL + LD (WORK_4),DE + POP HL + LD E,(HL) + INC HL + LD D,(HL) + INC HL + PUSH HL + LD HL,AUTO_3D2F + PUSH HL + PUSH DE + LD HL,RET_INS + PUSH HL + LD HL,(WORK_2) + LD DE,(WORK_4) + RET +; +; INIT DOS VARS +; +AUTO_2F90: ; INIT DATA DOS + LD HL,0FFFFh + LD (TIME_A),HL + LD (TIME_C),HL + LD (DISK_C),HL + CALL INIT_2X80_1 ; *** !!! *** +; LD (DISK_A),HL + XOR A + LD (FLAG_BOOT),A + LD (CONT_DISK),A + LD (INT_1_VAR),A + LD (DOS_ERR_2),A + LD (FL_NUMBER+1),A + LD A,255 + OUT (P_DOS_FF),A + LD (ERR_NR),A + LD (PDOS_COPY),A + LD (BUF_FLAG),A + LD A,201 + LD (RET_INS),A + LD A,0D0h + OUT (WG_COM),A + RET +; +; +; +AUTO_2FC6: + CALL AUTO_050C + LD A,(HL) + OR A + JP Z,AUTO_03AC + CP 1 + CALL Z,AUTO_0507 + RET NZ + JR AUTO_2FC6 +; +; +; +AUTO_2FD6: + LD B,1 + LD HL,BUFER + JP AUTO_1E67 +AUTO_2FDE: + PUSH HL + LD DE,(CONT_SEC) + CALL AUTO_2FD6 + LD A,(MED_LEN) + POP DE + OR A + RET Z + LD C,A + LD HL,BUFER + LDIR + RET +; +; COMANDS +; +AUTO_2FF3: + DB 207,42,208,209 + DB 230,210,239,248 + DB 254,190,244,213 + DB 247,211,212,255 + DB 52,236,56,240 + DB 214,'/' +AUTO_3008: + DW CMD_CAT + DW CMD_STAR + DW CMD_FORMAT + DW CMD_MOVE + + DW CMD_NEW + DW CMD_ERASE + DW CMD_LOAD + DW CMD_SAVE + + DW CMD_RETURN + DW CMD_PEEK + DW CMD_POKE + DW CMD_MERGE + + DW CMD_RUN + DW CMD_OPEN + DW CMD_CLOSE + DW CMD_COPY + + DW CMD_40 + DW CMD_GOTO + DW CMD_80 + DW CMD_LIST + + DW CMD_VERIFY + DW CMD_MSDOS +; +; +; +AUTO_3032: + LD HL,(E_LINE) + LD (ADR_DOS_COM),HL + LD (DOS_CH_ADR),HL + LD A,255 + LD (DOS_ERROR),A + LD HL,MED_LEN + JR AUTO_3054 +; LD (MED_START),HL ; 3 байта заняты списком команд +; nop +; nop +; nop +AUTO_3048: + LD (ADR_DOS_COM),HL + LD (DOS_CH_ADR),HL + CALL AUTO_30E1 + RET NZ + INC HL + INC HL +AUTO_3054: + JP REDIR_LINE_CODING +; LD (MED_START),HL +AUTO_3057: ; преобразование командной строки !!! + CALL AUTO_30A9 ; поиск слова + JR NZ,AUTO_3087 ; если нет, выход + EX DE,HL + INC DE + LD B,0 + LD HL,AUTO_31C8 + ADD HL,BC + LD A,(HL) + LD HL,(DOS_CH_ADR) + LD (HL),A + INC HL + EX DE,HL + RST RST20 + DW 019DDH + PUSH BC + RST RST20 + DW 019E8H + POP BC + LD A,(DOS_ERROR) + OR A + JR NZ,AUTO_3087 + LD HL,(MED_START) + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL + OR A + SBC HL,BC + EX DE,HL + LD (HL),D + DEC HL + LD (HL),E +AUTO_3087: + LD HL,(DOS_CH_ADR) + LD A,(HL) + CP 13 + RET Z + INC HL ; следующее слово + LD (DOS_CH_ADR),HL + LD A,(HL) + CP 13 + RET Z + CP '"' + JR NZ,AUTO_3057 +AUTO_309A: + INC HL + LD A,(HL) + CP 13 + RET Z + CP '"' + JR NZ,AUTO_309A + INC HL + LD (DOS_CH_ADR),HL + JR AUTO_3057 +AUTO_30A9: + LD HL,(DOS_CH_ADR) + LD DE,AUTO_30FD + LD C,0 +AUTO_30B1: + LD A,(HL) + AND 223 + LD B,A + OR A + JR NZ,AUTO_30BB + INC HL + JR AUTO_30B1 +AUTO_30BB: + LD A,(DE) + AND 128 + JR NZ,AUTO_30C8 + LD A,(DE) + CP B + JR NZ,AUTO_30D9 + INC HL + INC DE + JR AUTO_30B1 +AUTO_30C8: + LD A,(DE) + AND 127 + CP B + RET Z +AUTO_30CD: + INC C + LD HL,(DOS_CH_ADR) + INC DE + LD A,(DE) + CP 255 + JR NZ,AUTO_30B1 + OR A + RET +AUTO_30D9: + INC DE + LD A,(DE) + AND 128 + JR Z,AUTO_30D9 + JR AUTO_30CD +AUTO_30E1: + LD HL,(PPC) + INC HL + INC HL + LD A,H + OR L + JR Z,AUTO_30F4 + XOR A + LD (DOS_ERROR),A + DEC HL + DEC HL + RST RST20 + DW 0196EH + RET +AUTO_30F4: + LD A,255 + LD (DOS_ERROR),A + LD HL,(E_LINE) + RET +; +; +; +AUTO_30FD: + DB "SAVE",128, "SAV",197 ; 248,248 + DB "LOAD",128, "LOA",196 ; 239,239 + DB "RUN",128, "RU",206 ; 247,247 + DB "CAT",128, "CA",212 ; 207,207 + ; + DB "ERASE",128, "ERAS",197 ; 210,210 + DB "NEW",128, "NE",215 ; 230,230 + DB "MOVE",128, "MOV",197 ; 209,209 + DB "MERGE",128, "MERG",197 ; 213,213 + ; + DB "PEEK",128, "PEE",203 ; 190,190 + DB "POKE",128, "POK",197 ; 244,244 + DB "OPEN",131, "CLOSE",131 ; 211,212 + DB "CODE",128, "COD",197 ; 175,175 + ; + DB "RND",128, "RN",196 ; 165,165 + DB "DATA",128, "DAT",193 ; 228,228 + DB "SCREEN",4,132, "SCREEN",132 ; 170,170 + DB "COPY",128, "COP",217 ; 255,255 + ; + DB "FORMAT",128, "FORMA",212 ; 208,208 + DB "GOTO",128, "GOT",207 ; 236,236 + DB "LIST",128, "LIS",212 ; 240,240 + DB "LINE",128, "LIN",197 ; 202,202 + ; + DB "VERIFY",128, "VERIF",217 ; 214,214 + DB 255,255 + +AUTO_31C8: + DB 248,248 + DB 239,239 + DB 247,247 + DB 207,207 + + DB 210,210 + DB 230,230 + DB 209,209 + DB 213,213 + + DB 190,190 + DB 244,244 + DB 211,212 + DB 175,175 + + DB 165,165 + DB 228,228 + DB 170,170 + DB 255,255 + + DB 208,208 + DB 236,236 + DB 240,240 + DB 202,202 + + DB 214,214 + DB 0 +; +; +; +AUTO_31F3: + BLOCK #31F3 - $,255 + LD HL,(CHANS) + OR A + LD BC,BUFER + SBC HL,BC + RET + RST R38 + RST R38 + RST R38 +; +; +; +INIT_2X80: + JP AUTO_106E +INIT_2X80_1: + XOR A + LD (OPER_DISK),A + LD A,3Ch + LD (PDOS_COPY),A + OUT (P_DOS_FF),A + LD HL,8383h + LD (DISK_A),HL + LD (DISK_B),HL + LD (DISK_C),HL + LD (DISK_D),HL + LD HL,303H + LD (TIME_A),HL + LD (TIME_B),HL + LD (TIME_C),HL + LD (TIME_D),HL +; CALL INIT_SYS_PAGE + RET +; +; +; +LOAD_CR: + LD A,13 + LD (COM_LN_COPY),A + JP AUTO_021D +; +; +; +PRINT_TRK: + PUSH HL + PUSH BC + PUSH AF + PUSH DE + LD A,207 + LD (SCR_PL_L),A + LD A,15 + LD (SCR_POS_L),A + LD A,E + JR CONV1 +; +; +; +PRINT_LINE: + LD A,(HL) + CP 255 + RET Z + RST RST10 + INC HL + JR PRINT_LINE +; +; +; +CONV1: + LD B,'0' +CONV2: + SUB 10 + JR C,CONV3 + INC B + JR CONV2 +CONV3: + PUSH AF + LD A,B + RST RST10 + POP AF + ADD A,':' + RST RST10 + POP DE + POP AF + POP BC + POP HL + EI + JP Goto_trk + + +MSG_FORM: + DB 22,0,0,"Formating trk:",23,21," side:",255 +MSG_FILE_1: + DB 22,0,0,"File ",'"',19,1,255 +MSG_FILE_2: + DB 19,0,'"',13," exists.. Overwrite ? (y/n)",255 +MSG_DISK: + DB 22,0,0,"Disk : ",255 +MSG_FORM_2: + DB 22,0,0,"FORMAT:",23,12," 1 - Original" + DB 13,23,12," 2 - Turbo" + DB 13,23,12," 3 - Turbo/FAST" + DB 13,23,12," 4 - Quick",255 +; DB 13,23,12," 5 - RAM-Disk",255 +MSG_AT00: + DB 22,0,0,255 + +FORM_STATE: + CALL INPUT_TXT + CALL SINTAX_RET + LD HL,(CH_ADR) + LD A,(HL) + CP '"' + JR NZ,FORM_NO_NEXT + INC HL + LD A,(HL) +FORM_NO_NEXT: + CP 0Dh + RET Z + CP 80H + RET Z + JP NC,CMD_ERROR + CP ' ' + JP C,CMD_ERROR + AND 0DFh + LD (MED_START+1),A + RET +; +; +FORM_F01: + INC HL + LD A,(FL_LEN) + LD (HL),A + XOR A + RET +; +; +PRINT_SIDE1: + PUSH AF + LD A,'1' + JR PR_SIDE_X +PRINT_SIDE0: + PUSH AF + LD A,'0' +PR_SIDE_X: + PUSH AF + LD A,219 + LD (SCR_PL_L),A + LD A,27 + LD (SCR_POS_L),A + POP AF + RST RST10 + POP AF + JP AUTO_1FFD +; +; +; +OWERWR_1: + CALL LOW_CLS_AT00 + LD HL,MSG_FILE_1 + CALL PRINT_LINE + CALL PRINT_NAME + LD HL,MSG_FILE_2 + CALL PRINT_LINE + LD HL,AUTO_4000 +PAUSE_X01: + DEC HL + LD A,H + OR L + JR NZ,PAUSE_X01 + CALL AUTO_1052 + JR REWR_CONT1 + + + +PRINT_NAME: + LD A,(OPER_DISK) + ADD A,'A' + RST RST10 + LD A,':' + RST RST10 + LD HL,FL_NAME + JP AUTO_2938 + + +REWR_CONT1: + PUSH AF + CALL LOW_CLS_AT00 + POP AF + CP 'Y' + RET Z + CP 13 + RET + + + +SET_7_TYPE: + CALL AUTO_165D + LD HL,FL_TYPE + SET 7,(HL) + RET + +FORM_X02: + CALL LOW_CLS_AT00 + LD HL,MSG_FORM ; 'format track: side:' + CALL PRINT_LINE + CALL FORMAT_ALL ; программа форматирования ! + CALL LOW_CLS_AT00 + LD HL,MSG_DISK + CALL PRINT_LINE + RET + +FORM_X03: + LD A,(MED_START+1) + CP 15H + CALL AUTO_1C57 + CALL AUTO_1D97 + LD A,2 + RST RST20 + DW 01601H + LD HL,NEW_NAME + RST PRINT_LN + +FORM_X04: + CALL AUTO_106E + LD A,(MED_START+1) + AND A + JR NZ,FORM_NO_Q + CALL DISK_REDIR + CP 4 + LD A,14H + JR NC,FORM_NO_Q +FORM_X05: + CALL LOW_CLS_AT00 + LD HL,MSG_FORM_2 + CALL PRINT_LINE + CALL AUTO_1052 + AND A + JP Z,AUTO_01D3 +FORM_NO_Q: + LD (MED_START+1),A + SUB 11H + JR Z,F_ORIGINAL + JR C,FORM_X05 + DEC A + JR Z,F_TURBO + DEC A + JR Z,F_FAST + DEC A + JR Z,F_TURBO +; JR FORM_LOOP_X + +F_ORIGINAL: ; Original FORMAT + LD HL,AUTO_1FB9 +FORM_X07: + XOR A +FORM_X08: + LD (FL_LEN),A + LD (FL_START),HL + CALL LOW_CLS_AT00 + RET + +F_TURBO: ; Turbo FORMAT + LD HL,F_TURBO_DT + JR FORM_X07 + +F_FAST: ; Turbo/FAST FORMAT + LD A,1 + LD HL,F_TURBO_DT + JR FORM_X08 + +F_TURBO_DT: + DB 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + DB 16,1 + + +SUB_HL_14: + LD A,L + SUB 14 + LD L,A + RET NC + DEC H + RET + +LOW_CLS_AT00: + RST RST20 + DW 00D6EH + LD HL,MSG_AT00 + JP PRINT_LINE + +TYPE_FL_X00: + LD A,(FL_TYPE) + CP 'B' + JR Z,TYPE_FL_X01 + CP 'D' + JR Z,TYPE_FL_X01 + CP '#' + JR Z,TYPE_FL_X01 + LD A,'C' + LD (FL_TYPE),A +TYPE_FL_X01: + LD A,(DOS_ERROR) + RET + + +TYPE_FL_X02: + CALL AUTO_1CF0 + CALL Z,SET_7_TYPE +TYPE_FL_X03: + CALL RD_HEAD_DSK + CALL AUTO_1AC4 + JP AUTO_1B53 + +OWERWR_X01: + LD HL,FL_TYPE + BIT 7,(HL) + JR NZ,OWERWR_X02 + JP AUTO_1B59 +OWERWR_X02: + RES 7,(HL) + CALL OWERWR_1 + JP NZ,AUTO_01D3 + LD DE,(DOS_CH_ADR) + LD A,E + OR A + JR Z,OWERWR_X03 + INC D +OWERWR_X03: + LD A,(FL_SIZE) + CP D + JR NC,OWERWR_X04 + CALL AUTO_2926 + JR TYPE_FL_X03 +OWERWR_X04: + LD B,D + LD HL,(E_LINE) + LD (HL),170 + INC HL + LD DE,(AUTO_5CD1) + LD (HL),E + INC HL + LD (HL),D + LD C,6 + LD HL,(MED_LEN) + LD (FL_LEN),HL + LD HL,(MED_START) + LD (FL_START),HL + LD DE,(FL_PLACE) + CALL WRITE_DISK + LD BC,(FL_NUMBER) + CALL AUTO_1BBF + CALL AUTO_1665 + RET + + +LABEL_X00: + EI + JP AUTO_2F69 + +;/////////////////////////////////// + +SIDE_FORMAT: ; Для форматирования с номером стороны !!! + LD A,(PDOS_COPY) + BIT 5,A + JR Z,SIDE_OUTS + INC D +SIDE_OUTS: + IN A,(P_DOS_FF) + AND 192 + JR Z,SIDE_OUTS + RET M + OUT (C),D + RET +;************************************** + + INCLUDE 'trdos/tr_rmd_s.asz' + INCLUDE 'trdos/tr_msd_s.asz' + +;************************************** +; Дополнения к программе MOVE +;************************************** + +MOVE_X00: + LD HL,(FL_N_4) +MOVE_X01: + LD A,(HL) + CP 0 + RET Z + CP 1 + JR Z,MOVE_X02 + CALL MOVE_X03 +MOVE_X02: + LD DE,RST10 + ADD HL,DE + JR MOVE_X01 +MOVE_X03: + PUSH HL + LD DE,CONT_1+4 + ADD HL,DE + LD C,(HL) + INC HL + LD E,(HL) + INC HL + LD D,(HL) + LD HL,(MED_START) + AND A + SBC HL,DE + ADD HL,DE + JR Z,MOVE_X05 + LD (AUTO_5CD5),DE + LD A,C + LD (AUTO_5CD3),A + CALL AUTO_17A5 +MOVE_X04: + POP HL + PUSH HL + LD DE,(FL_N_2) + LD BC,RST10 + LDIR + LD (FL_N_2),DE + EX DE,HL + LD DE,(FL_NAME) + DEC HL + LD (HL),D + DEC HL + LD (HL),E + LD HL,(MED_START) + LD (FL_NAME),HL + LD HL,FL_N_6 + INC (HL) + POP HL + RET +MOVE_X05: + LD A,C + AND 15 + ADD A,E + BIT 4,A + RES 4,A + LD E,A + JR Z,MOVE_X06 + INC D +MOVE_X06: + LD A,C + RRC A + RRC A + RRC A + RRC A + AND 15 + ADD A,D + LD D,A + LD (MED_START),DE + JR MOVE_X04 + +;************************************* +EDIT_RET: + LD HL,(E_LINE) + LD (HL),254 + INC HL + LD (HL),13 + INC HL + LD (HL),80H + RET + +EDIT_X00: + RES 5,(IY+1) + RST RST20 ; ввод клавиши + DW 15D4h + CP 4 + JR Z,EDIT_RET + CP 7 + JR NZ,EDIT_X02 + CALL BEEP_1 + CALL AUTO_212A ; восстановление ком. строки + LD HL,(E_LINE) + LD A,13 + LD BC,0 + CPIR + DEC HL + LD (K_CUR),HL + INC HL + INC HL + LD (HL),128 +EDIT_X01: + RST RST20 ; ввод клавиши + DW 015D4H +EDIT_X02: + CALL BEEP_1 + CP 24 + JR NC,EDIT_X03 + CP 7 ; до 7 - ввод в буфер + JR C,EDIT_X03 + CP 16 ; от 7 до 16 - ред. строки + JR C,EDIT_X04 + RST RST20 ; ввод спец символов + DW 00F58H + JR EDIT_X01 +EDIT_X03: + RST RST20 ; станд. ввод в буфер редактирования + DW 00F81H + JR EDIT_X01 +EDIT_X04: + CP 7 ; на 7 - нет операции + JR Z,EDIT_X01 + CP 13 ; CR - возврат из ред. строки + RET Z + CP 10 ; вверх/вниз - нет операции + JR Z,EDIT_X01 + CP 11 + JR Z,EDIT_X01 + RST RST20 ; выполнить операцию редактирования + DW 00F92H + JR EDIT_X01 + + +EDIT_X05: + CALL AUTO_1D9F + JP AUTO_0239 + +;****************************************** + +RUS_PRINT: + AND '?' + LD L,A + LD H,0 + ADD HL,HL + ADD HL,HL + ADD HL,HL + LD BC,ZG_RUS + ADD HL,BC + DB 17 + SUB D + LD E,H + LD BC,RST08 + LDIR + LD A,201 + LD (AUTO_5C9A),A + JP AUTO_5C9A + +;************************************** + +; DSS 3900H,'#' + +;********************************** + DISPLAY "14k" + + BLOCK #3C00 - $,255 + +;********************************************* + +AUTO_3C00: + RST R38 + JR AUTO_3C06 + RST R38 + JR AUTO_3C09 +AUTO_3C06: + JP AUTO_3D00 +AUTO_3C09: + JP AUTO_3D03 + +;***************************** + BLOCK #3C10 - $,255 +;KBD_EXP: + LD A,D_ROM16OFF + OUT (CNF_PORT),A + RET + NOP + NOP + NOP +;KBD_OFF: + LD A,D_ROM16OFF + OUT (CNF_PORT),A + RET + NOP + NOP + NOP +;************************************ +READR_X: + CALL 3E08h + LD B,A + JP 1FEBh + + BLOCK #3C30 - $,255 + + IN A,(WG_COM) + RET + +;***************************** + +NEW_NAME: + DB 22,1,3,"* Sprinter TR-DOS v.7.01 *" + DB 22,3,6, "(c) 2001 PETERS PLUS" + DB 22,5,12, "* R.F.*" + DB 22,7,9, "BETA 4Mb",0 + +;***************************** + +;FORMATER: +; LD DE,(E_LINE) +; LD HL,TX_FORMAT +; LD BC,LEN_TX_F +; LDIR +; EX DE,HL +; JP CMD_NEW_EXEC +;TX_FORMAT: +; DB 247,'"C:FORMATER"' +;LEN_TX_F EQU $-TX_FORMAT +;****************************** +;****************************** +PRINT_RED: + PUSH HL + PUSH BC + PUSH DE + PUSH AF + CALL xchg_intf1 + POP AF + CALL AUTO_3D94 + CALL xchg_intf1 + POP DE + POP BC + POP HL + RET + +;******************************* + + BLOCK #3CC0 - $,255 +MAGIC_1: + PUSH AF + LD A,D_ROM16OFF + OUT (CNF_PORT),A + POP AF + JP MAGIC_2 +MAGIC_3: + PUSH AF + LD A,D_ROM16OFF + OUT (CNF_PORT),A + POP AF + RETN + +;******************************* +BEEP_1: + PUSH AF + LD HL,200 ; BEEP + LD D,H + LD E,(IY-1) + RST RST20 + DW 03B5h + + POP AF + RET + +; DSS 3CF0H,255 +;JP_HL_48: +; PUSH HL +;SW_ROM: +; PUSH AF +; LD A,0 +; OUT (CNF_PORT),A +; POP AF +; RET + +REDIR_3D13: + BIT 7,C + JP NZ,EXP_FNS ;#3FF8 EXP + BIT 6,C + JP Z,AUTO_283C + RES 6,C + JP HD_CMD ;#3FF0 ROM. DI + + BLOCK #3CFA - $,255 + +;***************************** + +AUTO_3CFA: + JP xchg_intf1 + +;****** 3D13 redirection ****** +AUTO_3CFD: + JP REDIR_3D13 +; JP AUTO_283C + +AUTO_3D00: + NOP + JR AUTO_3D31 +AUTO_3D03: + NOP + JR AUTO_3D1A +AUTO_3D06: + NOP + JP AUTO_25EF +AUTO_3D0A: + JP AUTO_244A + NOP +AUTO_3D0E: + JR AUTO_3D0A + NOP + JR AUTO_3CFA +;****** 3D13 point !!! **** + NOP + JR AUTO_3CFD +AUTO_3D16: + NOP + JP LABEL_X00 +AUTO_3D1A: + CALL AUTO_3D21 + PUSH HL + JP AUTO_016C +AUTO_3D21: + CALL AUTO_31F3 ; проверить была отведена память или нет + NOP + NOP + CALL C,AUTO_3D4C ; если нет, то вызвать программу иниц. + LD HL,RET_INS + RET + NOP + NOP +AUTO_3D2F: + NOP + RET +AUTO_3D31: + CALL AUTO_3D21 + PUSH HL + JP EDIT_X05 +AUTO_3D38: + XOR A + OUT (#F7),A ;????? + IN A,(#F7) ;????? + CP 30 + JR Z,AUTO_3D44 + CP 31 + RET NZ +.Size EQU $-AUTO_3D38 +AUTO_3D44: + RST RST08 + DB 49 ;!HARDCODE + LD A,1 + LD (INTERF_I),A + RET +.Size EQU $-AUTO_3D44 +AUTO_3D4C: + XOR A + OUT (P_DOS_FF),A + IN A,(#F6) ;????? + LD HL,AUTO_3D38 + LD DE,MEM_BOT + LD BC,AUTO_3D38.Size + AUTO_3D44.Size + LDIR + LD HL,AUTO_3D67 + PUSH HL + LD HL,AUTO_3D2F + PUSH HL + JP MEM_BOT + +AUTO_3D67: ; получить память для DOS + LD HL,AUTO_2F90 ; и вернуться в программу иниц. VARS + PUSH HL + LD HL,AUTO_3D2F + PUSH HL + LD HL,1655h ;!HARDCODE + PUSH HL + LD HL,AUTO_5BFF + PUSH HL + LD (HL),#C9 ; RET code + LD HL,P_RAMTOP+1 + LD BC,CONT_8+7 + RET +PRINT_CR: + LD A,13 +PRINT_SYM: + JP PRINT_RED + RST R38 + RST R38 + NOP + NOP + JP RUS_PRINT + RST R38 + RST R38 + RST R38 + NOP + NOP + RST R38 + RST R38 + RST R38 +AUTO_3D94: + RST RST20 + DW 00010H + RET +AUTO_3D98: + LD A,8 +WG93_EXE: + OUT (WG_COM),A +AUTO_3D9C: + PUSH HL + RST RST20 + DW 01F54H +;RET_1F54: + JR C,AUTO_3DA5 + RST RST20 + DW 01B7BH +AUTO_3DA5: + POP HL + IN A,(P_DOS_FF) + AND 128 + JR Z,AUTO_3D9C + RET +RESET_WG: + LD A,8 + CALL WG93_EXE + LD DE,START + IN A,(WG_COM) + AND 2 + LD B,A +AUTO_3DBA: + IN A,(WG_COM) + AND 2 + CP B + RET NZ + INC DE + LD A,E + OR D + JR NZ,AUTO_3DBA + JP AUTO_3EE7 +AUTO_3DC8: + LD A,(CONT_DISK) +SET_DISK: + JP RAMD_X12 +AUTO_3DCE: + LD HL,PDOS_COPY + LD C,A + LD A,3Ch + OR C + OUT (P_DOS_FF),A + LD (HL),A + CALL get_disk_time + AND 128 + JR Z,AUTO_3DFA + CALL RESET_WG + CALL test_disk + CALL get_disk_type + CP 255 + JR NZ,AUTO_3DFA + PUSH HL + CALL test_d_traks + POP HL + CP 'P' + LD A,0 + JR NZ,AUTO_3DF9 + LD A,128 +AUTO_3DF9: + LD (HL),A +AUTO_3DFA: + CALL READ_TRK_NUM +pause_50ms: + LD A,12 ;'P' +pause_1ms: + LD C,255 +AUTO_3E01: + DEC C + JR NZ,AUTO_3E01 + DEC A + JR NZ,pause_1ms + RET +get_disk_time: + LD DE,TIME_A +AUTO_3E0B: + LD HL,(OPER_DISK) + ADD HL,DE + LD A,(HL) + RET +get_disk_type: + LD DE,DISK_A + JR AUTO_3E0B +test_disk: + CALL get_disk_time + LD B,8 + LD C,4 +AUTO_3E1D: + LD (HL),B + LD A,8 + CALL WG93_EXE + LD A,' ' + LD B,11 + CALL Goto_trk + LD B,(HL) + LD A,1 + CALL Goto_trk + IN A,(WG_COM) + AND 4 + JR NZ,AUTO_3E3F + XOR A + CALL Goto_trk + IN A,(WG_COM) + AND 4 + RET NZ +AUTO_3E3F: + INC B + DEC C + RET Z + JR AUTO_3E1D +Goto_trk: + OUT (WG_DATA),A + LD A,B + OR 24 + JP WG93_EXE_ +Goto_trkx: + OUT (WG_DATA),A + PUSH BC + LD B,A + IN A,(WG_TRK) + CP B + POP BC + PUSH AF + LD A,B + OR 24 + CALL WG93_EXE_ + POP AF + RET Z + PUSH BC + CALL pause_50ms + POP BC + RET +Global_trk: + LD C,A + CALL set_side_0 + CALL get_disk_type + AND 2 + CALL NZ,made_trk_side2 + PUSH BC + BIT 7,(HL) + JR Z,AUTO_3E83 + BIT 0,(HL) + JR NZ,AUTO_3E83 + IN A,(WG_TRK) + CP C + JR Z,AUTO_3E82 + RLCA + OUT (WG_TRK),A + LD A,C + RLCA +AUTO_3E82: + LD C,A +AUTO_3E83: + CALL get_disk_time + LD B,A + IN A,(WG_TRK) + CP C + PUSH BC + CALL NZ,pause_50ms + POP BC + LD A,C + CALL Goto_trkx + POP BC + LD A,C + OUT (WG_TRK),A + LD A,(DRV_READY) + OR A + RET Z + XOR A + LD (DRV_READY),A +pause_750ms: + LD B,3 +AUTO_3EA2: + LD A,255 + CALL pause_1ms + DJNZ AUTO_3EA2 + RET +made_trk_side2: + LD A,C + OR A + RRA + LD C,A + RET NC + JP set_side_1 ; SET SIDE 1 +READ_ADR_LABL: + CALL set_side_0 ; SET SIDE 0 +READ_ADR_LABL2: + IN A,(WG_COM) + AND 128 + LD (DRV_READY),A + IN A,(WG_TRK) + LD H,A ; на текущую дорожку + CALL Goto_trk ; позиционирование +; CALL READR_X + LD C,127 + LD D,1 + DI + LD A,0C0h ; чтение адресной метки + OUT (WG_COM),A + PUSH BC + LD B,6 +AUTO_3ECE: + IN A,(P_DOS_FF) + AND 192 + JR NZ,AUTO_3EF2 + INC DE + LD A,E + OR D + JR NZ,AUTO_3ECE + DJNZ AUTO_3ECE + POP BC + EI + LD A,208 ; прервать выполнение + OUT (WG_COM),A + LD A,(AUTO_5CD1) + CP 255 + RET Z +AUTO_3EE7: + CALL set_error_26 + LD A,255 + LD (FLAG_BOOT),A + JP exit_No_disk +AUTO_3EF2: + POP BC + IN H,(C) +AUTO_3EF5: + IN A,(P_DOS_FF) + AND 192 + JR Z,AUTO_3EF5 + EI + RET M + DI + IN A,(WG_DATA) + JR AUTO_3EF5 +save_sector: + LD (SEC_NUM),A + RET +save_bufer: + LD (CONT_BUF_ADR),HL + RET +AUTO_3F0A: + LD A,160 + JR AUTO_3F10 +AUTO_3F0E: + LD A,128 +AUTO_3F10: + LD (COMAND_WG),A +AUTO_3F13: + LD D,10 +AUTO_3F15: + PUSH DE + DI + LD A,(SEC_NUM) + INC A + OUT (WG_SEC),A + LD HL,(CONT_BUF_ADR) + LD C,127 + LD A,(COMAND_WG) + OUT (WG_COM),A + CP 160 + PUSH AF + CALL Z,AUTO_3FBA + POP AF + CALL NZ,AUTO_3FD5 + POP DE + EI + IN A,(WG_COM) + LD B,A + AND 127 + RET Z +AUTO_3F39: + LD HL,READ_ONLY_MSG + AND '@' + JR NZ,AUTO_3F4B + LD A,B + AND 4 + JR Z,AUTO_3FA0 + DEC D + JR NZ,AUTO_3F15 +AUTO_3F48: + LD HL,DISK_ERROR_MSG +AUTO_3F4B: + LD A,208 + OUT (WG_COM),A + LD A,B + AND 1 + JP NZ,AUTO_3EE7 + IN A,(WG_TRK) + OR A + JR NZ,AUTO_3F5F + IN A,(WG_SEC) + CP 10 + RET Z +AUTO_3F5F: + PUSH HL + CALL AUTO_1D97 + POP HL + RST PRINT_LN + LD HL,AUTO_2A13 + RST PRINT_LN + IN A,(WG_TRK) + CALL AUTO_1DA3 + LD HL,AUTO_2A18 + RST PRINT_LN + IN A,(WG_SEC) + CALL AUTO_1DA3 + LD HL,AUTO_29FE + RST PRINT_LN +AUTO_3F7B: + CALL AUTO_1052 + CP 'I' + RET Z + CP 'R' + JR Z,AUTO_3F94 + CP 'A' + JR NZ,AUTO_3F7B + CALL set_error_26 + LD A,7 + LD (DOS_ERR_2),A + JP AUTO_01D3 +AUTO_3F94: + LD A,(CONT_TRK) + CALL Global_trk + CALL pause_750ms + JP AUTO_3F13 +AUTO_3FA0: + DEC D + JP Z,AUTO_3F48 + PUSH DE + CALL get_disk_time + AND 2 + JR NZ,AUTO_3FAD + INC (HL) +AUTO_3FAD: + CALL AUTO_3D98 + LD A,(CONT_TRK) + CALL Global_trk + POP DE + JP AUTO_3F15 +;****************************************** +; Sector Write !!! +;****************************************** +AUTO_3FBA: + LD B,4 +AUTO_3FBC: + IN A,(P_DOS_FF) + AND 192 + JR NZ,AUTO_3FD1 + INC DE + LD A,E + OR D + JR NZ,AUTO_3FBC + DJNZ AUTO_3FBC + RET +AUTO_3FCA: + IN A,(P_DOS_FF) + AND 192 + JR Z,AUTO_3FCA + RET M +AUTO_3FD1: + OUTI + JR AUTO_3FCA +;****************************************** +; Sector Read !!! +;****************************************** +AUTO_3FD5: + LD B,4 +AUTO_3FD7: + IN A,(P_DOS_FF) + AND 192 + JR NZ,AUTO_3FEC + INC DE + LD A,E + OR D + JR NZ,AUTO_3FD7 + DJNZ AUTO_3FD7 + RET +AUTO_3FE5: + IN A,(P_DOS_FF) + AND 192 + JR Z,AUTO_3FE5 + RET M +AUTO_3FEC: + INI + JR AUTO_3FE5 +;****************************************** + + +;****************************************** + BLOCK #3FF0-$, #FF + +HD_CMD: + PUSH AF + DI + LD A,D_ROM16ON + OUT (SYS_PORT_ON),A + POP AF + RET +;****************************************** + + +;****************************************** + BLOCK #3FF8-$, #FF +EXP_FNS: + PUSH AF + DI + XOR A ; D_ROM16OFF + OUT (SYS_PORT_ON),A + POP AF + RET +;****************************************** + BLOCK #4000-$, #FF + DISPLAY "16k..ALL!" +; + diff --git a/SP__48.ASM b/SP__48.ASM new file mode 100644 index 0000000..194685d --- /dev/null +++ b/SP__48.ASM @@ -0,0 +1,20413 @@ +; ************************************** +; *** SPECTRUM 128 ROM 1 DISASSEMBLY *** +; ************************************** + + INCLUDE 'shared_includes/constants/sp2000.inc' + +; The Spectrum ROMs are copyright Amstrad, who have kindly given permission +; to reverse engineer and publish ROM disassemblies. + + +; ===== +; NOTES +; ===== + +; ------------ +; Release Date +; ------------ +; 13th December 2011 + +; ------------------------ +; Disassembly Contributors +; ------------------------ +; Geoff Wearmouth (gwearmouth-AT-hotmail.com) +; Paul Farrow (www.fruitcake.plus.com) + + +; ================= +; ASSEMBLER DEFINES +; ================= + +;TASM directives: + +; #define DEFB .BYTE +; #define DEFW .WORD +; #define DEFM .TEXT +; #DEFINE DEFS .FILL +; #define END .END +; #define EQU .EQU +; #define ORG .ORG + +; The Sinclair Interface1 ROM written by Dr. Ian Logan calls numerous +; routines in this ROM. Non-standard entry points have a label beginning +; with X. + + ORG $0000 + +;***************************************** +;** Part 1. RESTART ROUTINES AND TABLES ** +;***************************************** + +; ----------- +; THE 'START' +; ----------- +; At switch on, the Z80 chip is in interrupt mode 0. +; This location can also be 'called' to reset the machine. +; Typically with PRINT USR 0. + +;; START +L0000: DI ; disable interrupts. + XOR A ; signal coming from START. + LD DE,$FFFF ; top of possible physical RAM. + JP L11CB ; jump forward to common code at START-NEW. + +; ------------------- +; THE 'ERROR' RESTART +; ------------------- +; The error pointer is made to point to the position of the error to enable +; the editor to show the error if it occurred during syntax checking. +; It is used at 37 places in the program. +; An instruction fetch on address $0008 may page in a peripheral ROM +; such as the Sinclair Interface 1 or Disciple Disk Interface. +; This was not however an original design concept and not all errors pass +; through here. + +;; ERROR-1 +L0008: LD HL,($5C5D) ; fetch the character address from CH_ADD. + LD ($5C5F),HL ; copy it to the error pointer X_PTR. + JR L0053 ; forward to continue at ERROR-2. + +; ----------------------------- +; THE 'PRINT CHARACTER' RESTART +; ----------------------------- +; The A register holds the code of the character that is to be sent to +; the output stream of the current channel. +; The alternate register set is used to output a character in the A register +; so there is no need to preserve any of the current registers (HL,DE,BC). +; This restart is used 21 times. + +;; PRINT-A +L0010: JP L15F2 ; jump forward to continue at PRINT-A-2. + +; --- + +X0013: DEFB $FF ; this byte is used by the SPECTRUM command in + ; ROM 0 to generate an error report "0 OK". + + DEFB $FF, $FF ; four unused locations. + DEFB $FF, $FF ; + +; ------------------------------- +; THE 'COLLECT CHARACTER' RESTART +; ------------------------------- +; The contents of the location currently addressed by CH_ADD are fetched. +; A return is made if the value represents a character that has +; relevance to the BASIC parser. Otherwise CH_ADD is incremented and the +; tests repeated. CH_ADD will be addressing somewhere - +; 1) in the BASIC program area during line execution. +; 2) in workspace if evaluating, for example, a string expression. +; 3) in the edit buffer if parsing a direct command or a new BASIC line. +; 4) in workspace if accepting input but not that from INPUT LINE. + +;; GET-CHAR +L0018: LD HL,($5C5D) ; fetch the address from CH_ADD. + LD A,(HL) ; use it to pick up current character. + +;; TEST-CHAR +L001C: CALL L007D ; routine SKIP-OVER tests if the character + RET NC ; is relevant. Return if it is so. + +; ------------------------------------ +; THE 'COLLECT NEXT CHARACTER' RESTART +; ------------------------------------ +; As the BASIC commands and expressions are interpreted, this routine is +; called repeatedly to step along the line. It is used 83 times. + +;; NEXT-CHAR +L0020: CALL L0074 ; routine CH-ADD+1 fetches the next immediate + ; character. + JR L001C ; jump back to TEST-CHAR until a valid + ; character is found. + +; --- + + DEFB $FF, $FF, $FF ; unused + +; ----------------------- +; THE 'CALCULATE' RESTART +; ----------------------- +; This restart enters the Spectrum's internal, floating-point, +; stack-based, FORTH-like language. +; It is further used recursively from within the calculator. +; It is used on 77 occasions. +;; FP-CALC +L0028: JP L335B ; jump forward to the CALCULATE routine. + +; --- + + DEFB $FF, $FF, $FF ; spare - note that on the ZX81, space being a + DEFB $FF, $FF ; little cramped, these same locations were + ; used for the five-byte end-calc literal. + +; ------------------------------ +; THE 'CREATE BC SPACES' RESTART +; ------------------------------ +; This restart is used on only 12 occasions to create BC spaces +; between workspace and the calculator stack. + +;; BC-SPACES +L0030: PUSH BC ; save number of spaces. + LD HL,($5C61) ; fetch WORKSP. + PUSH HL ; save address of workspace. + JP L169E ; jump forward to continuation code RESERVE. + +; -------------------------------- +; THE 'MASKABLE INTERRUPT' ROUTINE +; -------------------------------- +; This routine increments the Spectrum's three-byte FRAMES counter +; fifty times a second (sixty times a second in the USA ). +; Both this routine and the called KEYBOARD subroutine use +; the IY register to access system variables and flags so a user-written +; program must disable interrupts to make use of the IY register. + +;; MASK-INT +L0038: PUSH AF ; save the registers. + PUSH HL ; but not IY unfortunately. + LD HL,($5C78) ; fetch two bytes at FRAMES1. + INC HL ; increment lowest two bytes of counter. + LD ($5C78),HL ; place back in FRAMES1. + LD A,H ; test if the result + OR L ; was zero. + JR NZ,L0048 ; forward to KEY-INT if not. + + INC (IY+$40) ; otherwise increment FRAMES3 the third byte. + +; now save the rest of the main registers and read and decode the keyboard. + +;; KEY-INT +L0048: PUSH BC ; save the other + PUSH DE ; main registers. + CALL L386E ; Spectrum 128 patch: read the keypad and keyboard + ; in the process of reading a key-press. +L004D: POP DE ; + POP BC ; restore registers. + + POP HL ; + POP AF ; + EI ; enable interrupts. + RET ; return. + +; --------------------- +; THE 'ERROR-2' ROUTINE +; --------------------- +; A continuation of the code at 0008. +; The error code is stored and after clearing down stacks, +; an indirect jump is made to MAIN-4, etc. to handle the error. + +;; ERROR-2 +L0053: POP HL ; drop the return address - the location + ; after the RST 08H instruction. + LD L,(HL) ; fetch the error code that follows. + ; (nice to see this instruction used.) + +; Note. this entry point is used when out of memory at REPORT-4. +; The L register has been loaded with the report code but X-PTR is not +; updated. + +;; ERROR-3 +L0055: LD (IY+$00),L ; store it in the system variable ERR_NR. + LD SP,($5C3D) ; ERR_SP points to an error handler on the + ; machine stack. There may be a hierarchy + ; of routines. + ; to MAIN-4 initially at base. + ; or REPORT-G on line entry. + ; or ED-ERROR when editing. + ; or ED-FULL during ed-enter. + ; or IN-VAR-1 during runtime input etc. + + JP L16C5 ; jump to SET-STK to clear the calculator + ; stack and reset MEM to usual place in the + ; systems variables area. + ; and then indirectly to MAIN-4, etc. + +; --- + + DEFB $FF, $FF, $FF ; unused locations + DEFB $FF, $FF, $FF ; before the fixed-position + DEFB $FF ; NMI routine. + +; ------------------------------------ +; THE 'NON-MASKABLE INTERRUPT' ROUTINE +; ------------------------------------ +; There is no NMI switch on the standard Spectrum. +; When activated, a location in the system variables is tested +; and if the contents are zero a jump made to that location else +; a return is made. Perhaps a disabled development feature but +; if the logic was reversed, no program would be safe from +; copy-protection and the Spectrum would have had no software base. +; The location NMIADD was later used by Interface 1 for other purposes. +; On later Spectrums, and the Brazilian Spectrum, the logic of this +; routine was reversed. + +;; RESET +L0066: PUSH AF ; save the + PUSH HL ; registers. + LD HL,($5CB0) ; fetch the system variable NMIADD. + LD A,H ; test address + OR L ; for zero. + JR NZ,L0070 ; skip to NO-RESET if NOT ZERO + + JP (HL) ; jump to routine ( i.e. L0000 ) + +;; NO-RESET +L0070: POP HL ; restore the + POP AF ; registers. + RETN ; return to previous interrupt state. + +; --------------------------- +; THE 'CH ADD + 1' SUBROUTINE +; --------------------------- +; This subroutine is called from RST 20, and three times from elsewhere +; to fetch the next immediate character following the current valid character +; address and update the associated system variable. +; The entry point TEMP-PTR1 is used from the SCANNING routine. +; Both TEMP-PTR1 and TEMP-PTR2 are used by the READ command routine. + +;; CH-ADD+1 +L0074: LD HL,($5C5D) ; fetch address from CH_ADD. + +;; TEMP-PTR1 +L0077: INC HL ; increase the character address by one. + +;; TEMP-PTR2 +L0078: LD ($5C5D),HL ; update CH_ADD with character address. + +X007B: LD A,(HL) ; load character to A from HL. + RET ; and return. + +; -------------------------- +; THE 'SKIP OVER' SUBROUTINE +; -------------------------- +; This subroutine is called once from RST 18 to skip over white-space and +; other characters irrelevant to the parsing of a BASIC line etc. . +; Initially the A register holds the character to be considered +; and HL holds its address which will not be within quoted text +; when a BASIC line is parsed. +; Although the 'tab' and 'at' characters will not appear in a BASIC line, +; they could be present in a string expression, and in other situations. +; Note. although white-space is usually placed in a program to indent loops +; and make it more readable, it can also be used for the opposite effect and +; spaces may appear in variable names although the parser never sees them. +; It is this routine that helps make the variables 'Anum bEr5 3BUS' and +; 'a number 53 bus' appear the same to the parser. + +;; SKIP-OVER +L007D: CP $21 ; test if higher than space. + RET NC ; return with carry clear if so. + + CP $0D ; carriage return ? + RET Z ; return also with carry clear if so. + + ; all other characters have no relevance + ; to the parser and must be returned with + ; carry set. + + CP $10 ; test if 0-15d + RET C ; return, if so, with carry set. + + CP $18 ; test if 24-32d + CCF ; complement carry flag. + RET C ; return with carry set if so. + + ; now leaves 16d-23d + + INC HL ; all above have at least one extra character + ; to be stepped over. + + CP $16 ; controls 22d ('at') and 23d ('tab') have two. + JR C,L0090 ; forward to SKIPS with ink, paper, flash, + ; bright, inverse or over controls. + ; Note. the high byte of tab is for RS232 only. + ; it has no relevance on this machine. + + INC HL ; step over the second character of 'at'/'tab'. + +;; SKIPS +L0090: SCF ; set the carry flag + LD ($5C5D),HL ; update the CH_ADD system variable. + RET ; return with carry set. + + +; ------------------ +; THE 'TOKEN TABLES' +; ------------------ +; The tokenized characters 134d (RND) to 255d (COPY) are expanded using +; this table. The last byte of a token is inverted to denote the end of +; the word. The first is an inverted step-over byte. + +;; TKN-TABLE +L0095: DC '?' + DC "RND" + DC "INKEY$" + DC 'PI' + DC 'FN' + DC "POINT" + DC "SCREEN$" + DC "ATTR" + DC 'AT' + DC "TAB" + DC "VAL$" + DC "CODE" + DC "VAL" + DC "LEN" + DC "SIN" + DC "COS" + DC "TAN" + DC "ASN" + DC "ACS" + DC "ATN" + DC 'LN' + DC "EXP" + DC "INT" + DC "SQR" + DC "SGN" + DC "ABS" + DC "PEEK" + DC 'IN' + DC "USR" + DC "STR$" + DC "CHR$" + DC "NOT" + DC "BIN" + +; The previous 32 function-type words are printed without a leading space +; The following have a leading space if they begin with a letter + + DEFB 'O','R'+$80 + DEFM "AN" + DEFB 'D'+$80 + DEFB $3C,'='+$80 ; <= + DEFB $3E,'='+$80 ; >= + DEFB $3C,$3E+$80 ; <> + DEFM "LIN" + DEFB 'E'+$80 + DEFM "THE" + DEFB 'N'+$80 + DEFB 'T','O'+$80 + DEFM "STE" + DEFB 'P'+$80 + DEFM "DEF F" + DEFB 'N'+$80 + DEFM "CA" + DEFB 'T'+$80 + DEFM "FORMA" + DEFB 'T'+$80 + DEFM "MOV" + DEFB 'E'+$80 + DEFM "ERAS" + DEFB 'E'+$80 + DEFM "OPEN " + DEFB '#'+$80 + DEFM "CLOSE " + DEFB '#'+$80 + DEFM "MERG" + DEFB 'E'+$80 + DEFM "VERIF" + DEFB 'Y'+$80 + DEFM "BEE" + DEFB 'P'+$80 + DEFM "CIRCL" + DEFB 'E'+$80 + DEFM "IN" + DEFB 'K'+$80 + DEFM "PAPE" + DEFB 'R'+$80 + DEFM "FLAS" + DEFB 'H'+$80 + DEFM "BRIGH" + DEFB 'T'+$80 + DEFM "INVERS" + DEFB 'E'+$80 + DEFM "OVE" + DEFB 'R'+$80 + DEFM "OU" + DEFB 'T'+$80 + DEFM "LPRIN" + DEFB 'T'+$80 + DEFM "LLIS" + DEFB 'T'+$80 + DEFM "STO" + DEFB 'P'+$80 + DEFM "REA" + DEFB 'D'+$80 + DEFM "DAT" + DEFB 'A'+$80 + DEFM "RESTOR" + DEFB 'E'+$80 + DEFM "NE" + DEFB 'W'+$80 + DEFM "BORDE" + DEFB 'R'+$80 + DEFM "CONTINU" + DEFB 'E'+$80 + DEFM "DI" + DEFB 'M'+$80 + DEFM "RE" + DEFB 'M'+$80 + DEFM "FO" + DEFB 'R'+$80 + DEFM "GO T" + DEFB 'O'+$80 + DEFM "GO SU" + DEFB 'B'+$80 + DEFM "INPU" + DEFB 'T'+$80 + DEFM "LOA" + DEFB 'D'+$80 + DEFM "LIS" + DEFB 'T'+$80 + DEFM "LE" + DEFB 'T'+$80 + DEFM "PAUS" + DEFB 'E'+$80 + DEFM "NEX" + DEFB 'T'+$80 + DEFM "POK" + DEFB 'E'+$80 + DEFM "PRIN" + DEFB 'T'+$80 + DEFM "PLO" + DEFB 'T'+$80 + DEFM "RU" + DEFB 'N'+$80 + DEFM "SAV" + DEFB 'E'+$80 + DEFM "RANDOMIZ" + DEFB 'E'+$80 + DEFB 'I','F'+$80 + DEFM "CL" + DEFB 'S'+$80 + DEFM "DRA" + DEFB 'W'+$80 + DEFM "CLEA" + DEFB 'R'+$80 + DEFM "RETUR" + DEFB 'N'+$80 + DEFM "COP" + DEFB 'Y'+$80 + +; ---------------- +; THE 'KEY' TABLES +; ---------------- +; These six look-up tables are used by the keyboard reading routine +; to decode the key values. + +; The first table contains the maps for the 39 keys of the standard +; 40-key Spectrum keyboard. The remaining key [SHIFT $27] is read directly. +; The keys consist of the 26 upper-case alphabetic characters, the 10 digit +; keys and the space, ENTER and symbol shift key. +; Unshifted alphabetic keys have $20 added to the value. +; The keywords for the main alphabetic keys are obtained by adding $A5 to +; the values obtained from this table. + +;; MAIN-KEYS +L0205: DEFB $42 ; B + DEFB $48 ; H + DEFB $59 ; Y + DEFB $36 ; 6 + DEFB $35 ; 5 + DEFB $54 ; T + DEFB $47 ; G + DEFB $56 ; V + DEFB $4E ; N + DEFB $4A ; J + DEFB $55 ; U + DEFB $37 ; 7 + DEFB $34 ; 4 + DEFB $52 ; R + DEFB $46 ; F + DEFB $43 ; C + DEFB $4D ; M + DEFB $4B ; K + DEFB $49 ; I + DEFB $38 ; 8 + DEFB $33 ; 3 + DEFB $45 ; E + DEFB $44 ; D + DEFB $58 ; X + DEFB $0E ; SYMBOL SHIFT + DEFB $4C ; L + DEFB $4F ; O + DEFB $39 ; 9 + DEFB $32 ; 2 + DEFB $57 ; W + DEFB $53 ; S + DEFB $5A ; Z + DEFB $20 ; SPACE + DEFB $0D ; ENTER + DEFB $50 ; P + DEFB $30 ; 0 + DEFB $31 ; 1 + DEFB $51 ; Q + DEFB $41 ; A + + +;; E-UNSHIFT +; The 26 unshifted extended mode keys for the alphabetic characters. +; The green keywords on the original keyboard. +L022C: DEFB $E3 ; READ + DEFB $C4 ; BIN + DEFB $E0 ; LPRINT + DEFB $E4 ; DATA + DEFB $B4 ; TAN + DEFB $BC ; SGN + DEFB $BD ; ABS + DEFB $BB ; SQR + DEFB $AF ; CODE + DEFB $B0 ; VAL + DEFB $B1 ; LEN + DEFB $C0 ; USR + DEFB $A7 ; PI + DEFB $A6 ; INKEY$ + DEFB $BE ; PEEK + DEFB $AD ; TAB + DEFB $B2 ; SIN + DEFB $BA ; INT + DEFB $E5 ; RESTORE + DEFB $A5 ; RND + DEFB $C2 ; CHR$ + DEFB $E1 ; LLIST + DEFB $B3 ; COS + DEFB $B9 ; EXP + DEFB $C1 ; STR$ + DEFB $B8 ; LN + + +;; EXT-SHIFT +; The 26 shifted extended mode keys for the alphabetic characters. +; The red keywords below keys on the original keyboard. +L0246: DEFB $7E ; ~ + DEFB $DC ; BRIGHT + DEFB $DA ; PAPER + DEFB $5C ; \ + DEFB $B7 ; ATN + DEFB $7B ; { + DEFB $7D ; } + DEFB $D8 ; CIRCLE + DEFB $BF ; IN + DEFB $AE ; VAL$ + DEFB $AA ; SCREEN$ + DEFB $AB ; ATTR + DEFB $DD ; INVERSE + DEFB $DE ; OVER + DEFB $DF ; OUT + DEFB $7F ; (Copyright character) + DEFB $B5 ; ASN + DEFB $D6 ; VERIFY + DEFB $7C ; | + DEFB $D5 ; MERGE + DEFB $5D ; ] + DEFB $DB ; FLASH + DEFB $B6 ; ACS + DEFB $D9 ; INK + DEFB $5B ; [ + DEFB $D7 ; BEEP + + +;; CTL-CODES +; The ten control codes assigned to the top line of digits when the shift +; key is pressed. +L0260: DEFB $0C ; DELETE + DEFB $07 ; EDIT + DEFB $06 ; CAPS LOCK + DEFB $04 ; TRUE VIDEO + DEFB $05 ; INVERSE VIDEO + DEFB $08 ; CURSOR LEFT + DEFB $0A ; CURSOR DOWN + DEFB $0B ; CURSOR UP + DEFB $09 ; CURSOR RIGHT + DEFB $0F ; GRAPHICS + + +;; SYM-CODES +; The 26 red symbols assigned to the alphabetic characters of the keyboard. +; The ten single-character digit symbols are converted without the aid of +; a table using subtraction and minor manipulation. +L026A: DEFB $E2 ; STOP + DEFB $2A ; * + DEFB $3F ; ? + DEFB $CD ; STEP + DEFB $C8 ; >= + DEFB $CC ; TO + DEFB $CB ; THEN + DEFB $5E ; ^ + DEFB $AC ; AT + DEFB $2D ; - + DEFB $2B ; + + DEFB $3D ; = + DEFB $2E ; . + DEFB $2C ; , + DEFB $3B ; ; + DEFB $22 ; " + DEFB $C7 ; <= + DEFB $3C ; < + DEFB $C3 ; NOT + DEFB $3E ; > + DEFB $C5 ; OR + DEFB $2F ; / + DEFB $C9 ; <> + DEFB $60 ; pound + DEFB $C6 ; AND + DEFB $3A ; : + +;; E-DIGITS +; The ten keywords assigned to the digits in extended mode. +; The remaining red keywords below the keys. +L0284: DEFB $D0 ; FORMAT + DEFB $CE ; DEF FN + DEFB $A8 ; FN + DEFB $CA ; LINE + DEFB $D3 ; OPEN# + DEFB $D4 ; CLOSE# + DEFB $D1 ; MOVE + DEFB $D2 ; ERASE + DEFB $A9 ; POINT + DEFB $CF ; CAT + + +;******************************* +;** Part 2. KEYBOARD ROUTINES ** +;******************************* + +; Using shift keys and a combination of modes the Spectrum 40-key keyboard +; can be mapped to 256 input characters + +; --------------------------------------------------------------------------- +; +; 0 1 2 3 4 -Bits- 4 3 2 1 0 +; PORT PORT +; +; F7FE [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] | [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ] EFFE +; ^ | v +; FBFE [ Q ] [ W ] [ E ] [ R ] [ T ] | [ Y ] [ U ] [ I ] [ O ] [ P ] DFFE +; ^ | v +; FDFE [ A ] [ S ] [ D ] [ F ] [ G ] | [ H ] [ J ] [ K ] [ L ] [ ENT ] BFFE +; ^ | v +; FEFE [SHI] [ Z ] [ X ] [ C ] [ V ] | [ B ] [ N ] [ M ] [sym] [ SPC ] 7FFE +; ^ $27 $18 v +; Start End +; 00100111 00011000 +; +; --------------------------------------------------------------------------- +; The above map may help in reading. +; The neat arrangement of ports means that the B register need only be +; rotated left to work up the left hand side and then down the right +; hand side of the keyboard. When the reset bit drops into the carry +; then all 8 half-rows have been read. Shift is the first key to be +; read. The lower six bits of the shifts are unambiguous. + +; ------------------------------- +; THE 'KEYBOARD SCANNING' ROUTINE +; ------------------------------- +; from keyboard and s-inkey$ +; returns 1 or 2 keys in DE, most significant shift first if any +; key values 0-39 else 255 + +;; KEY-SCAN +L028E: LD L,$2F ; initial key value + ; valid values are obtained by subtracting + ; eight five times. + LD DE,$FFFF ; a buffer to receive 2 keys. + + LD BC,$FEFE ; the commencing port address + ; B holds 11111110 initially and is also + ; used to count the 8 half-rows +;; KEY-LINE +L0296: IN A,(C) ; read the port to A - bits will be reset + ; if a key is pressed else set. + CPL ; complement - pressed key-bits are now set + AND $1F ; apply 00011111 mask to pick up the + ; relevant set bits. + + JR Z,L02AB ; forward to KEY-DONE if zero and therefore + ; no keys pressed in row at all. + + LD H,A ; transfer row bits to H + LD A,L ; load the initial key value to A + +;; KEY-3KEYS +L029F: INC D ; now test the key buffer + RET NZ ; if we have collected 2 keys already + ; then too many so quit. + +;; KEY-BITS +L02A1: SUB $08 ; subtract 8 from the key value + ; cycling through key values (top = $27) + ; e.g. 2F> 27>1F>17>0F>07 + ; 2E> 26>1E>16>0E>06 + SRL H ; shift key bits right into carry. + JR NC,L02A1 ; back to KEY-BITS if not pressed + ; but if pressed we have a value (0-39d) + + LD D,E ; transfer a possible previous key to D + LD E,A ; transfer the new key to E + JR NZ,L029F ; back to KEY-3KEYS if there were more + ; set bits - H was not yet zero. + +;; KEY-DONE +L02AB: DEC L ; cycles 2F>2E>2D>2C>2B>2A>29>28 for + ; each half-row. + RLC B ; form next port address e.g. FEFE > FDFE + JR C,L0296 ; back to KEY-LINE if still more rows to do. + + LD A,D ; now test if D is still FF ? + INC A ; if it is zero we have at most 1 key + ; range now $01-$28 (1-40d) + RET Z ; return if one key or no key. + + CP $28 ; is it capsshift (was $27) ? + RET Z ; return if so. + + CP $19 ; is it symbol shift (was $18) ? + RET Z ; return also + + LD A,E ; now test E + LD E,D ; but first switch + LD D,A ; the two keys. + CP $18 ; is it symbol shift ? + RET ; return (with zero set if it was). + ; but with symbol shift now in D + +; ------------------------------ +; Scan keyboard and decode value +; ------------------------------ +; from interrupt 50 times a second +; + +;; KEYBOARD +L02BF: CALL L028E ; routine KEY-SCAN + RET NZ ; return if invalid combinations + +; then decrease the counters within the two key-state maps +; as this could cause one to become free. +; if the keyboard has not been pressed during the last five interrupts +; then both sets will be free. + + + LD HL,$5C00 ; point to KSTATE-0 + +;; K-ST-LOOP +L02C6: BIT 7,(HL) ; is it free ? ($FF) + JR NZ,L02D1 ; forward to K-CH-SET if so + + INC HL ; address 5-counter + DEC (HL) ; decrease counter + DEC HL ; step back + JR NZ,L02D1 ; forward to K-CH-SET if not at end of count + + LD (HL),$FF ; else mark it free. + +;; K-CH-SET +L02D1: LD A,L ; store low address byte. + LD HL,$5C04 ; point to KSTATE-4 + ; (ld l, $04) + CP L ; have 2 been done ? + JR NZ,L02C6 ; back to K-ST-LOOP to consider this 2nd set + +; now the raw key (0-38) is converted to a main key (uppercase). + + CALL L031E ; routine K-TEST to get main key in A + RET NC ; return if single shift + + LD HL,$5C00 ; point to KSTATE-0 + CP (HL) ; does it match ? + JR Z,L0310 ; forward to K-REPEAT if so + +; if not consider the second key map. + + EX DE,HL ; save kstate-0 in de + LD HL,$5C04 ; point to KSTATE-4 + CP (HL) ; does it match ? + JR Z,L0310 ; forward to K-REPEAT if so + +; having excluded a repeating key we can now consider a new key. +; the second set is always examined before the first. + + BIT 7,(HL) ; is it free ? + JR NZ,L02F1 ; forward to K-NEW if so. + + EX DE,HL ; bring back kstate-0 + BIT 7,(HL) ; is it free ? + RET Z ; return if not. + ; as we have a key but nowhere to put it yet. + +; continue or jump to here if one of the buffers was free. + +;; K-NEW +L02F1: LD E,A ; store key in E + LD (HL),A ; place in free location + INC HL ; advance to interrupt counter + LD (HL),$05 ; and initialize to 5 + INC HL ; advance to delay + LD A,($5C09) ; pick up system variable REPDEL + LD (HL),A ; and insert that for first repeat delay. + INC HL ; advance to last location of state map. + + LD C,(IY+$07) ; pick up MODE (3 bytes) + LD D,(IY+$01) ; pick up FLAGS (3 bytes) + PUSH HL ; save state map location + ; Note. could now have used. + ; ld l,$41; ld c,(hl); ld l,$3B; ld d,(hl). + ; six and two threes of course. + CALL L0333 ; routine K-DECODE + POP HL ; restore map pointer + LD (HL),A ; put decoded key in last location of map. + +;; K-END +L0308: LD ($5C08),A ; update LASTK system variable. + SET 5,(IY+$01) ; update FLAGS - signal new key. + RET ; done + +; --------------------------- +; THE 'REPEAT KEY' SUBROUTINE +; --------------------------- +; A possible repeat has been identified. HL addresses the raw (main) key. +; The last location holds the decoded key (from the first context). + +;; K-REPEAT +L0310: INC HL ; advance + LD (HL),$05 ; maintain interrupt counter at 5 + INC HL ; advance + DEC (HL) ; decrease REPDEL value. + RET NZ ; return if not yet zero. + + LD A,($5C0A) ; REPPER + LD (HL),A ; but for subsequent repeats REPPER will be used. + INC HL ; advance + ; + LD A,(HL) ; pick up the key decoded possibly in another + ; context. + JR L0308 ; back to K-END + +; -------------- +; Test key value +; -------------- +; also called from s-inkey$ +; begin by testing for a shift with no other. + +;; K-TEST +L031E: LD B,D ; load most significant key to B + ; will be $FF if not shift. + LD D,$00 ; and reset D to index into main table + LD A,E ; load least significant key from E + CP $27 ; is it higher than 39d i.e. FF + RET NC ; return with just a shift (in B now) + + CP $18 ; is it symbol shift ? + JR NZ,L032C ; forward to K-MAIN if not + +; but we could have just symbol shift and no other + + BIT 7,B ; is other key $FF (ie not shift) + RET NZ ; return with solitary symbol shift + + +;; K-MAIN +L032C: LD HL,L0205 ; address: MAIN-KEYS + ADD HL,DE ; add offset 0-38 + LD A,(HL) ; pick up main key value + SCF ; set carry flag + RET ; return (B has other key still) + +; ----------------- +; Keyboard decoding +; ----------------- +; also called from s-inkey$ + +;; K-DECODE +L0333: LD A,E ; pick up the stored main key + CP $3A ; an arbitrary point between digits and letters + JR C,L0367 ; forward to K-DIGIT with digits, space, enter. + + DEC C ; decrease MODE ( 0='KLC', 1='E', 2='G') + + JP M,L034F ; to K-KLC-LET if was zero + + JR Z,L0341 ; to K-E-LET if was 1 for extended letters. + +; proceed with graphic codes. +; Note. should selectively drop return address if code > 'U' ($55). +; i.e. abort the KEYBOARD call. +; e.g. cp 'V'; jr c addit; pop af; ;;addit etc. (5 bytes of instruction). +; (s-inkey$ never gets into graphics mode.) + +;; addit + ADD A,$4F ; add offset to augment 'A' to graphics A say. + RET ; return. + ; Note. ( but [GRAPH] V gives RND, etc ). + +; --- + +; the jump was to here with extended mode with uppercase A-Z. + +;; K-E-LET +L0341: LD HL,L022C-$41 ; base address of E-UNSHIFT L022c + ; ( $01EB in standard ROM ) + INC B ; test B is it empty i.e. not a shift + JR Z,L034A ; forward to K-LOOK-UP if neither shift + + LD HL,L0246-$41 ; Address: $0205 L0246-$41 EXT-SHIFT base + +;; K-LOOK-UP +L034A: LD D,$00 ; prepare to index + ADD HL,DE ; add the main key value + LD A,(HL) ; pick up other mode value + RET ; return + +; --- + +; the jump was here with mode = 0 + +;; K-KLC-LET +L034F: LD HL,L026A-$41 ; prepare base of sym-codes + BIT 0,B ; shift=$27 sym-shift=$18 + JR Z,L034A ; back to K-LOOK-UP with symbol-shift + + BIT 3,D ; test FLAGS is it 'K' mode (from OUT-CURS) + JR Z,L0364 ; skip to K-TOKENS if so + + BIT 3,(IY+$30) ; test FLAGS2 - consider CAPS LOCK ? + RET NZ ; return if so with main code. + + INC B ; is shift being pressed ? + ; result zero if not + RET NZ ; return if shift pressed. + + ADD A,$20 ; else convert the code to lower case. + RET ; return. + +; --- + +; the jump was here for tokens + +;; K-TOKENS +L0364: ADD A,$A5 ; add offset to main code so that 'A' + ; becomes 'NEW' etc. + RET ; return + +; --- + +; the jump was here with digits, space, enter and symbol shift (< $xx) + +;; K-DIGIT +L0367: CP $30 ; is it '0' or higher ? + RET C ; return with space, enter and symbol-shift + + DEC C ; test MODE (was 0='KLC', 1='E', 2='G') + JP M,L039D ; jump to K-KLC-DGT if was 0. + + JR NZ,L0389 ; forward to K-GRA-DGT if mode was 2. + +; continue with extended digits 0-9. + + LD HL,L0284-$30 ; $0254 - base of E-DIGITS + BIT 5,B ; test - shift=$27 sym-shift=$18 + JR Z,L034A ; to K-LOOK-UP if sym-shift + + CP $38 ; is character '8' ? + JR NC,L0382 ; to K-8-&-9 if greater than '7' + + SUB $20 ; reduce to ink range $10-$17 + INC B ; shift ? + RET Z ; return if not. + + ADD A,$08 ; add 8 to give paper range $18 - $1F + RET ; return + +; --- + +; 89 + +;; K-8-&-9 +L0382: SUB $36 ; reduce to 02 and 03 bright codes + INC B ; test if shift pressed. + RET Z ; return if not. + + ADD A,$FE ; subtract 2 setting carry + RET ; to give 0 and 1 flash codes. + +; --- + +; graphics mode with digits + +;; K-GRA-DGT +L0389: LD HL,L0260-$30 ; $0230 base address of CTL-CODES + + CP $39 ; is key '9' ? + JR Z,L034A ; back to K-LOOK-UP - changed to $0F, GRAPHICS. + + CP $30 ; is key '0' ? + JR Z,L034A ; back to K-LOOK-UP - changed to $0C, delete. + +; for keys '0' - '7' we assign a mosaic character depending on shift. + + AND $07 ; convert character to number. 0 - 7. + ADD A,$80 ; add offset - they start at $80 + + INC B ; destructively test for shift + RET Z ; and return if not pressed. + + XOR $0F ; toggle bits becomes range $88-$8F + RET ; return. + +; --- + +; now digits in 'KLC' mode + +;; K-KLC-DGT +L039D: INC B ; return with digit codes if neither + RET Z ; shift key pressed. + + BIT 5,B ; test for caps shift. + + LD HL,L0260-$30 ; prepare base of table CTL-CODES. + JR NZ,L034A ; back to K-LOOK-UP if shift pressed. + +; must have been symbol shift + + SUB $10 ; for ASCII most will now be correct + ; on a standard typewriter. + CP $22 ; but '@' is not - see below. + JR Z,L03B2 ; forward to to K-@-CHAR if so + + CP $20 ; '_' is the other one that fails + RET NZ ; return if not. + + LD A,$5F ; substitute ASCII '_' + RET ; return. + +; --- + +;; K-@-CHAR +L03B2: LD A,$40 ; substitute ASCII '@' + RET ; return. + + +; ------------------------------------------------------------------------ +; The Spectrum Input character keys. One or two are abbreviated. +; From $00 Flash 0 to $FF COPY. The routine above has decoded all these. + +; | 00 Fl0| 01 Fl1| 02 Br0| 03 Br1| 04 In0| 05 In1| 06 CAP| 07 EDT| +; | 08 LFT| 09 RIG| 0A DWN| 0B UP | 0C DEL| 0D ENT| 0E SYM| 0F GRA| +; | 10 Ik0| 11 Ik1| 12 Ik2| 13 Ik3| 14 Ik4| 15 Ik5| 16 Ik6| 17 Ik7| +; | 18 Pa0| 19 Pa1| 1A Pa2| 1B Pa3| 1C Pa4| 1D Pa5| 1E Pa6| 1F Pa7| +; | 20 SP | 21 ! | 22 " | 23 # | 24 $ | 25 % | 26 & | 27 ' | +; | 28 ( | 29 ) | 2A * | 2B + | 2C , | 2D - | 2E . | 2F / | +; | 30 0 | 31 1 | 32 2 | 33 3 | 34 4 | 35 5 | 36 6 | 37 7 | +; | 38 8 | 39 9 | 3A : | 3B ; | 3C < | 3D = | 3E > | 3F ? | +; | 40 @ | 41 A | 42 B | 43 C | 44 D | 45 E | 46 F | 47 G | +; | 48 H | 49 I | 4A J | 4B K | 4C L | 4D M | 4E N | 4F O | +; | 50 P | 51 Q | 52 R | 53 S | 54 T | 55 U | 56 V | 57 W | +; | 58 X | 59 Y | 5A Z | 5B [ | 5C \ | 5D ] | 5E ^ | 5F _ | +; | 60 ukp| 61 a | 62 b | 63 c | 64 d | 65 e | 66 f | 67 g | +; | 68 h | 69 i | 6A j | 6B k | 6C l | 6D m | 6E n | 6F o | +; | 70 p | 71 q | 72 r | 73 s | 74 t | 75 u | 76 v | 77 w | +; | 78 x | 79 y | 7A z | 7B { | 7C | | 7D } | 7E ~ | 7F (c)| +; | 80 128| 81 129| 82 130| 83 131| 84 132| 85 133| 86 134| 87 135| +; | 88 136| 89 137| 8A 138| 8B 139| 8C 140| 8D 141| 8E 142| 8F 143| +; | 90 [A]| 91 [B]| 92 [C]| 93 [D]| 94 [E]| 95 [F]| 96 [G]| 97 [H]| +; | 98 [I]| 99 [J]| 9A [K]| 9B [L]| 9C [M]| 9D [N]| 9E [O]| 9F [P]| +; | A0 [Q]| A1 [R]| A2 [S]| A3 [T]| A4 [U]| A5 RND| A6 IK$| A7 PI | +; | A8 FN | A9 PNT| AA SC$| AB ATT| AC AT | AD TAB| AE VL$| AF COD| +; | B0 VAL| B1 LEN| B2 SIN| B3 COS| B4 TAN| B5 ASN| B6 ACS| B7 ATN| +; | B8 LN | B9 EXP| BA INT| BB SQR| BC SGN| BD ABS| BE PEK| BF IN | +; | C0 USR| C1 ST$| C2 CH$| C3 NOT| C4 BIN| C5 OR | C6 AND| C7 <= | +; | C8 >= | C9 <> | CA LIN| CB THN| CC TO | CD STP| CE DEF| CF CAT| +; | D0 FMT| D1 MOV| D2 ERS| D3 OPN| D4 CLO| D5 MRG| D6 VFY| D7 BEP| +; | D8 CIR| D9 INK| DA PAP| DB FLA| DC BRI| DD INV| DE OVR| DF OUT| +; | E0 LPR| E1 LLI| E2 STP| E3 REA| E4 DAT| E5 RES| E6 NEW| E7 BDR| +; | E8 CON| E9 DIM| EA REM| EB FOR| EC GTO| ED GSB| EE INP| EF LOA| +; | F0 LIS| F1 LET| F2 PAU| F3 NXT| F4 POK| F5 PRI| F6 PLO| F7 RUN| +; | F8 SAV| F9 RAN| FA IF | FB CLS| FC DRW| FD CLR| FE RET| FF CPY| + +; Note that for simplicity, Sinclair have located all the control codes +; below the space character. +; ASCII DEL, $7F, has been made a copyright symbol. +; Also $60, '`', not used in BASIC but used in other languages, has been +; allocated the local currency symbol for the relevant country - +; ukp in most Spectrums. + +; ------------------------------------------------------------------------ + +;********************************** +;** Part 3. LOUDSPEAKER ROUTINES ** +;********************************** + + +; Documented by Alvin Albrecht. + + +; ------------------------------ +; Routine to control loudspeaker +; ------------------------------ +; Outputs a square wave of given duration and frequency +; to the loudspeaker. +; Enter with: DE = #cycles - 1 +; HL = tone period as described next +; +; The tone period is measured in T states and consists of +; three parts: a coarse part (H register), a medium part +; (bits 7..2 of L) and a fine part (bits 1..0 of L) which +; contribute to the waveform timing as follows: +; +; coarse medium fine +; duration of low = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3) +; duration of hi = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3) +; Tp = tone period = 236 + 2048*H + 32*(L>>2) + 8*(L&0x3) +; = 236 + 2048*H + 8*L = 236 + 8*HL +; +; As an example, to output five seconds of middle C (261.624 Hz): +; (a) Tone period = 1/261.624 = 3.822ms +; (b) Tone period in T-States = 3.822ms*fCPU = 13378 +; where fCPU = clock frequency of the CPU = 3.5 MHz +; (c) Find H and L for desired tone period: +; HL = (Tp - 236) / 8 = (13378 - 236) / 8 = 1643 = 0x066B +; (d) Tone duration in cycles = 5s/3.822ms = 1308 cycles +; DE = 1308 - 1 = 0x051B +; +; The resulting waveform has a duty ratio of exactly 50%. +; +; +;; BEEPER +L03B5: DI ; Disable Interrupts so they don't disturb timing + LD A,L ; + SRL L ; + SRL L ; L = medium part of tone period + CPL ; + AND $03 ; A = 3 - fine part of tone period + LD C,A ; + LD B,$00 ; + LD IX,L03D1 ; Address: BE-IX+3 + ADD IX,BC ; IX holds address of entry into the loop + ; the loop will contain 0-3 NOPs, implementing + ; the fine part of the tone period. + LD A,($5C48) ; BORDCR + AND $38 ; bits 5..3 contain border colour + RRCA ; border colour bits moved to 2..0 + RRCA ; to match border bits on port #FE + RRCA ; + OR $08 ; bit 3 set (tape output bit on port #FE) + ; for loud sound output +;; BE-IX+3 +L03D1: NOP ;(4) ; optionally executed NOPs for small + ; adjustments to tone period +;; BE-IX+2 +L03D2: NOP ;(4) ; + +;; BE-IX+1 +L03D3: NOP ;(4) ; + +;; BE-IX+0 +L03D4: INC B ;(4) ; + INC C ;(4) ; + +;; BE-H&L-LP +L03D6: DEC C ;(4) ; timing loop for duration of + JR NZ,L03D6 ;(12/7); high or low pulse of waveform + + LD C,$3F ;(7) ; + DEC B ;(4) ; + JP NZ,L03D6 ;(10) ; to BE-H&L-LP + + XOR $10 ;(7) ; toggle output beep bit + OUT ($FE),A ;(11) ; output pulse + LD B,H ;(4) ; B = coarse part of tone period + LD C,A ;(4) ; save port #FE output byte + BIT 4,A ;(8) ; if new output bit is high, go + JR NZ,L03F2 ;(12/7); to BE-AGAIN + + LD A,D ;(4) ; one cycle of waveform has completed + OR E ;(4) ; (low->low). if cycle countdown = 0 + JR Z,L03F6 ;(12/7); go to BE-END + + LD A,C ;(4) ; restore output byte for port #FE + LD C,L ;(4) ; C = medium part of tone period + DEC DE ;(6) ; decrement cycle count + JP (IX) ;(8) ; do another cycle + +;; BE-AGAIN ; halfway through cycle +L03F2: LD C,L ;(4) ; C = medium part of tone period + INC C ;(4) ; adds 16 cycles to make duration of high = duration of low + JP (IX) ;(8) ; do high pulse of tone + +;; BE-END +L03F6: EI ; Enable Interrupts + RET ; + + +; ------------------- +; Handle BEEP command +; ------------------- +; BASIC interface to BEEPER subroutine. +; Invoked in BASIC with: +; BEEP dur, pitch +; where dur = duration in seconds +; pitch = # of semitones above/below middle C +; +; Enter with: pitch on top of calculator stack +; duration next on calculator stack +; +;; beep +L03F8: RST 28H ;; FP-CALC + DEFB $31 ;;duplicate ; duplicate pitch + DEFB $27 ;;int ; convert to integer + DEFB $C0 ;;st-mem-0 ; store integer pitch to memory 0 + DEFB $03 ;;subtract ; calculate fractional part of pitch = fp_pitch - int_pitch + DEFB $34 ;;stk-data ; push constant + DEFB $EC ;;Exponent: $7C, Bytes: 4 ; constant = 0.05762265 + DEFB $6C,$98,$1F,$F5 ;;($6C,$98,$1F,$F5) + DEFB $04 ;;multiply ; compute: + DEFB $A1 ;;stk-one ; 1 + 0.05762265 * fraction_part(pitch) + DEFB $0F ;;addition + DEFB $38 ;;end-calc ; leave on calc stack + + LD HL,$5C92 ; MEM-0: number stored here is in 16 bit integer format (pitch) + ; 0, 0/FF (pos/neg), LSB, MSB, 0 + ; LSB/MSB is stored in two's complement + ; In the following, the pitch is checked if it is in the range -128<=p<=127 + LD A,(HL) ; First byte must be zero, otherwise + AND A ; error in integer conversion + JR NZ,L046C ; to REPORT-B + + INC HL ; + LD C,(HL) ; C = pos/neg flag = 0/FF + INC HL ; + LD B,(HL) ; B = LSB, two's complement + LD A,B ; + RLA ; + SBC A,A ; A = 0/FF if B is pos/neg + CP C ; must be the same as C if the pitch is -128<=p<=127 + JR NZ,L046C ; if no, error REPORT-B + + INC HL ; if -128<=p<=127, MSB will be 0/FF if B is pos/neg + CP (HL) ; verify this + JR NZ,L046C ; if no, error REPORT-B + ; now we know -128<=p<=127 + LD A,B ; A = pitch + 60 + ADD A,$3C ; if -60<=pitch<=67, + JP P,L0425 ; goto BE-i-OK + + JP PO,L046C ; if pitch <= 67 goto REPORT-B + ; lower bound of pitch set at -60 + +;; BE-I-OK ; here, -60<=pitch<=127 + ; and A=pitch+60 -> 0<=A<=187 + +L0425: LD B,$FA ; 6 octaves below middle C + +;; BE-OCTAVE ; A=# semitones above 5 octaves below middle C +L0427: INC B ; increment octave + SUB $0C ; 12 semitones = one octave + JR NC,L0427 ; to BE-OCTAVE + + ADD A,$0C ; A = # semitones above C (0-11) + PUSH BC ; B = octave displacement from middle C, 2's complement: -5<=B<=10 + LD HL,L046E ; Address: semi-tone + CALL L3406 ; routine LOC-MEM + ; HL = 5*A + $046E + CALL L33B4 ; routine STACK-NUM + ; read FP value (freq) from semitone table (HL) and push onto calc stack + + RST 28H ;; FP-CALC + DEFB $04 ;;multiply mult freq by 1 + 0.0576 * fraction_part(pitch) stacked earlier + ;; thus taking into account fractional part of pitch. + ;; the number 0.0576*frequency is the distance in Hz to the next + ;; note (verify with the frequencies recorded in the semitone + ;; table below) so that the fraction_part of the pitch does + ;; indeed represent a fractional distance to the next note. + DEFB $38 ;;end-calc HL points to first byte of fp num on stack = middle frequency to generate + + POP AF ; A = octave displacement from middle C, 2's complement: -5<=A<=10 + ADD A,(HL) ; increase exponent by A (equivalent to multiplying by 2^A) + LD (HL),A ; + + RST 28H ;; FP-CALC + DEFB $C0 ;;st-mem-0 ; store frequency in memory 0 + DEFB $02 ;;delete ; remove from calc stack + DEFB $31 ;;duplicate ; duplicate duration (seconds) + DEFB $38 ;;end-calc + + CALL L1E94 ; routine FIND-INT1 ; FP duration to A + CP $0B ; if dur > 10 seconds, + JR NC,L046C ; goto REPORT-B + + ;;; The following calculation finds the tone period for HL and the cycle count + ;;; for DE expected in the BEEPER subroutine. From the example in the BEEPER comments, + ;;; + ;;; HL = ((fCPU / f) - 236) / 8 = fCPU/8/f - 236/8 = 437500/f -29.5 + ;;; DE = duration * frequency - 1 + ;;; + ;;; Note the different constant (30.125) used in the calculation of HL + ;;; below. This is probably an error. + + RST 28H ;; FP-CALC + DEFB $E0 ;;get-mem-0 ; push frequency + DEFB $04 ;;multiply ; result1: #cycles = duration * frequency + DEFB $E0 ;;get-mem-0 ; push frequency + DEFB $34 ;;stk-data ; push constant + DEFB $80 ;;Exponent $93, Bytes: 3 ; constant = 437500 + DEFB $43,$55,$9F,$80 ;;($55,$9F,$80,$00) + DEFB $01 ;;exchange ; frequency on top + DEFB $05 ;;division ; 437500 / frequency + DEFB $34 ;;stk-data ; push constant + DEFB $35 ;;Exponent: $85, Bytes: 1 ; constant = 30.125 + DEFB $71 ;;($71,$00,$00,$00) + DEFB $03 ;;subtract ; result2: tone_period(HL) = 437500 / freq - 30.125 + DEFB $38 ;;end-calc + + CALL L1E99 ; routine FIND-INT2 + PUSH BC ; BC = tone_period(HL) + CALL L1E99 ; routine FIND-INT2, BC = #cycles to generate + POP HL ; HL = tone period + LD D,B ; + LD E,C ; DE = #cycles + LD A,D ; + OR E ; + RET Z ; if duration = 0, skip BEEP and avoid 65536 cycle + ; boondoggle that would occur next + DEC DE ; DE = #cycles - 1 + JP L03B5 ; to BEEPER + +; --- + + +;; REPORT-B +L046C: RST 08H ; ERROR-1 + DEFB $0A ; Error Report: Integer out of range + + + +; --------------- +; Semi-tone table +; --------------- +; +; Holds frequencies corresponding to semitones in middle octave. +; To move n octaves higher or lower, frequencies are multiplied by 2^n. + +;; semi-tone five byte fp decimal freq note (middle) +L046E: DEFB $89, $02, $D0, $12, $86; 261.625565290 C + DEFB $89, $0A, $97, $60, $75; 277.182631135 C# + DEFB $89, $12, $D5, $17, $1F; 293.664768100 D + DEFB $89, $1B, $90, $41, $02; 311.126983881 D# + DEFB $89, $24, $D0, $53, $CA; 329.627557039 E + DEFB $89, $2E, $9D, $36, $B1; 349.228231549 F + DEFB $89, $38, $FF, $49, $3E; 369.994422674 F# + DEFB $89, $43, $FF, $6A, $73; 391.995436072 G + DEFB $89, $4F, $A7, $00, $54; 415.304697513 G# + DEFB $89, $5C, $00, $00, $00; 440.000000000 A + DEFB $89, $69, $14, $F6, $24; 466.163761616 A# + DEFB $89, $76, $F1, $10, $05; 493.883301378 B + + +;**************************************** +;** Part 4. CASSETTE HANDLING ROUTINES ** +;**************************************** + +; These routines begin with the service routines followed by a single +; command entry point. +; The first of these service routines is a curiosity. + +; ----------------------- +; THE 'ZX81 NAME' ROUTINE +; ----------------------- +; This routine fetches a filename in ZX81 format and is not used by the +; cassette handling routines in this ROM. + +;; zx81-name +L04AA: CALL L24FB ; routine SCANNING to evaluate expression. + LD A,($5C3B) ; fetch system variable FLAGS. + ADD A,A ; test bit 7 - syntax, bit 6 - result type. + JP M,L1C8A ; to REPORT-C if not string result + ; 'Nonsense in BASIC'. + + POP HL ; drop return address. + RET NC ; return early if checking syntax. + + PUSH HL ; re-save return address. + CALL L2BF1 ; routine STK-FETCH fetches string parameters. + LD H,D ; transfer start of filename + LD L,E ; to the HL register. + DEC C ; adjust to point to last character and + RET M ; return if the null string. + ; or multiple of 256! + + ADD HL,BC ; find last character of the filename. + ; and also clear carry. + SET 7,(HL) ; invert it. + RET ; return. + +; ========================================= +; +; PORT 254 ($FE) +; +; spk mic { border } +; ___ ___ ___ ___ ___ ___ ___ ___ +; PORT | | | | | | | | | +; 254 | | | | | | | | | +; $FE |___|___|___|___|___|___|___|___| +; 7 6 5 4 3 2 1 0 +; + +; ---------------------------------- +; Save header and program/data bytes +; ---------------------------------- +; This routine saves a section of data. It is called from SA-CTRL to save the +; seventeen bytes of header data. It is also the exit route from that routine +; when it is set up to save the actual data. +; On entry - +; HL points to start of data. +; IX points to descriptor. +; The accumulator is set to $00 for a header, $FF for data. + +;; SA-BYTES +L04C2: LD HL,L053F ; address: SA/LD-RET + PUSH HL ; is pushed as common exit route. + ; however there is only one non-terminal exit + ; point. + + LD HL,$1F80 ; a timing constant H=$1F, L=$80 + ; inner and outer loop counters + ; a five second lead-in is used for a header. + + BIT 7,A ; test one bit of accumulator. + ; (AND A ?) + JR Z,L04D0 ; skip to SA-FLAG if a header is being saved. + +; else is data bytes and a shorter lead-in is used. + + LD HL,$0C98 ; another timing value H=$0C, L=$98. + ; a two second lead-in is used for the data. + + +;; SA-FLAG +L04D0: EX AF,AF' ; save flag + INC DE ; increase length by one. + DEC IX ; decrease start. + + DI ; disable interrupts + + LD A,$02 ; select red for border, microphone bit on. + LD B,A ; also does as an initial slight counter value. + +;; SA-LEADER +L04D8: DJNZ L04D8 ; self loop to SA-LEADER for delay. + ; after initial loop, count is $A4 (or $A3) + + OUT ($FE),A ; output byte $02/$0D to tape port. + + XOR $0F ; switch from RED (mic on) to CYAN (mic off). + + LD B,$A4 ; hold count. also timed instruction. + + DEC L ; originally $80 or $98. + ; but subsequently cycles 256 times. + JR NZ,L04D8 ; back to SA-LEADER until L is zero. + +; the outer loop is counted by H + + DEC B ; decrement count + DEC H ; originally twelve or thirty-one. + JP P,L04D8 ; back to SA-LEADER until H becomes $FF + +; now send a synch pulse. At this stage mic is off and A holds value +; for mic on. +; A synch pulse is much shorter than the steady pulses of the lead-in. + + LD B,$2F ; another short timed delay. + +;; SA-SYNC-1 +L04EA: DJNZ L04EA ; self loop to SA-SYNC-1 + + OUT ($FE),A ; switch to mic on and red. + LD A,$0D ; prepare mic off - cyan + LD B,$37 ; another short timed delay. + +;; SA-SYNC-2 +L04F2: DJNZ L04F2 ; self loop to SA-SYNC-2 + + OUT ($FE),A ; output mic off, cyan border. + LD BC,$3B0E ; B=$3B time(*), C=$0E, YELLOW, MIC OFF. + +; + + EX AF,AF' ; restore saved flag + ; which is 1st byte to be saved. + + LD L,A ; and transfer to L. + ; the initial parity is A, $FF or $00. + JP L0507 ; JUMP forward to SA-START -> + ; the mid entry point of loop. + +; ------------------------- +; During the save loop a parity byte is maintained in H. +; the save loop begins by testing if reduced length is zero and if so +; the final parity byte is saved reducing count to $FFFF. + +;; SA-LOOP +L04FE: LD A,D ; fetch high byte + OR E ; test against low byte. + JR Z,L050E ; forward to SA-PARITY if zero. + + LD L,(IX+$00) ; load currently addressed byte to L. + +;; SA-LOOP-P +L0505: LD A,H ; fetch parity byte. + XOR L ; exclusive or with new byte. + +; -> the mid entry point of loop. + +;; SA-START +L0507: LD H,A ; put parity byte in H. + LD A,$01 ; prepare blue, mic=on. + SCF ; set carry flag ready to rotate in. + JP L0525 ; JUMP forward to SA-8-BITS -8-> + +; --- + +;; SA-PARITY +L050E: LD L,H ; transfer the running parity byte to L and + JR L0505 ; back to SA-LOOP-P + ; to output that byte before quitting normally. + +; --- + +; entry point to save yellow part of bit. +; a bit consists of a period with mic on and blue border followed by +; a period of mic off with yellow border. +; Note. since the DJNZ instruction does not affect flags, the zero flag is used +; to indicate which of the two passes is in effect and the carry maintains the +; state of the bit to be saved. + +;; SA-BIT-2 +L0511: LD A,C ; fetch 'mic on and yellow' which is + ; held permanently in C. + BIT 7,B ; set the zero flag. B holds $3E. + +; entry point to save 1 entire bit. For first bit B holds $3B(*). +; Carry is set if saved bit is 1. zero is reset NZ on entry. + +;; SA-BIT-1 +L0514: DJNZ L0514 ; self loop for delay to SA-BIT-1 + + JR NC,L051C ; forward to SA-OUT if bit is 0. + +; but if bit is 1 then the mic state is held for longer. + + LD B,$42 ; set timed delay. (66 decimal) + +;; SA-SET +L051A: DJNZ L051A ; self loop to SA-SET + ; (roughly an extra 66*13 clock cycles) + +;; SA-OUT +L051C: OUT ($FE),A ; blue and mic on OR yellow and mic off. + + LD B,$3E ; set up delay + JR NZ,L0511 ; back to SA-BIT-2 if zero reset NZ (first pass) + +; proceed when the blue and yellow bands have been output. + + DEC B ; change value $3E to $3D. + XOR A ; clear carry flag (ready to rotate in). + INC A ; reset zero flag ie. NZ. + +; -8-> + +;; SA-8-BITS +L0525: RL L ; rotate left through carry + ; C<76543210 + +; now test if byte counter has reached $FFFF. + + LD A,D ; fetch high byte + INC A ; increment. + JP NZ,L04FE ; JUMP to SA-LOOP if more bytes. + + LD B,$3B ; a final delay. + +;; SA-DELAY +L053C: DJNZ L053C ; self loop to SA-DELAY + + RET ; return - - > + +; -------------------------------------------------- +; Reset border and check BREAK key for LOAD and SAVE +; -------------------------------------------------- +; the address of this routine is pushed on the stack prior to any load/save +; operation and it handles normal completion with the restoration of the +; border and also abnormal termination when the break key, or to be more +; precise the space key is pressed during a tape operation. +; - - > + +;; SA/LD-RET +L053F: PUSH AF ; preserve accumulator throughout. + LD A,($5C48) ; fetch border colour from BORDCR. + AND $38 ; mask off paper bits. + RRCA ; rotate + RRCA ; to the + RRCA ; range 0-7. + + OUT ($FE),A ; change the border colour. + + LD A,$7F ; read from port address $7FFE the + IN A,($FE) ; row with the space key at outside. + + RRA ; test for space key pressed. + EI ; enable interrupts + JR C,L0554 ; forward to SA/LD-END if not + + +;; REPORT-Da +L0552: RST 08H ; ERROR-1 + DEFB $0C ; Error Report: BREAK - CONT repeats + +; --- + +;; SA/LD-END +L0554: POP AF ; restore the accumulator. + RET ; return. + +; ------------------------------------ +; Load header or block of information +; ------------------------------------ +; This routine is used to load bytes and on entry A is set to $00 for a +; header or to $FF for data. IX points to the start of receiving location +; and DE holds the length of bytes to be loaded. If, on entry the carry flag +; is set then data is loaded, if reset then it is verified. + +;; LD-BYTES +L0556: INC D ; reset the zero flag without disturbing carry. + EX AF,AF' ; preserve entry flags. + DEC D ; restore high byte of length. + + DI ; disable interrupts + + LD A,$0F ; make the border white and mic off. + OUT ($FE),A ; output to port. + + LD HL,L053F ; Address: SA/LD-RET + PUSH HL ; is saved on stack as terminating routine. + +; the reading of the EAR bit (D6) will always be preceded by a test of the +; space key (D0), so store the initial post-test state. + + IN A,($FE) ; read the ear state - bit 6. + RRA ; rotate to bit 5. + AND $20 ; isolate this bit. + OR $02 ; combine with red border colour. + LD C,A ; and store initial state long-term in C. + CP A ; set the zero flag. + +; + +;; LD-BREAK +L056B: RET NZ ; return if at any time space is pressed. + +;; LD-START +L056C: CALL L05E7 ; routine LD-EDGE-1 + JR NC,L056B ; back to LD-BREAK with time out and no + ; edge present on tape. + +; but continue when a transition is found on tape. + + LD HL,$0415 ; set up 16-bit outer loop counter for + ; approx 1 second delay. + +;; LD-WAIT +L0574: DJNZ L0574 ; self loop to LD-WAIT (for 256 times) + + DEC HL ; decrease outer loop counter. + LD A,H ; test for + OR L ; zero. + JR NZ,L0574 ; back to LD-WAIT, if not zero, with zero in B. + +; continue after delay with H holding zero and B also. +; sample 256 edges to check that we are in the middle of a lead-in section. + + CALL L05E3 ; routine LD-EDGE-2 + JR NC,L056B ; back to LD-BREAK + ; if no edges at all. + +;; LD-LEADER +L0580: LD B,$9C ; set timing value. + CALL L05E3 ; routine LD-EDGE-2 + JR NC,L056B ; back to LD-BREAK if time-out + + LD A,$C6 ; two edges must be spaced apart. + CP B ; compare + JR NC,L056C ; back to LD-START if too close together for a + ; lead-in. + + INC H ; proceed to test 256 edged sample. + JR NZ,L0580 ; back to LD-LEADER while more to do. + +; sample indicates we are in the middle of a two or five second lead-in. +; Now test every edge looking for the terminal synch signal. + +;; LD-SYNC +L058F: LD B,$C9 ; initial timing value in B. + CALL L05E7 ; routine LD-EDGE-1 + JR NC,L056B ; back to LD-BREAK with time-out. + + LD A,B ; fetch augmented timing value from B. + CP $D4 ; compare + JR NC,L058F ; back to LD-SYNC if gap too big, that is, + ; a normal lead-in edge gap. + +; but a short gap will be the synch pulse. +; in which case another edge should appear before B rises to $FF + + CALL L05E7 ; routine LD-EDGE-1 + RET NC ; return with time-out. + +; proceed when the synch at the end of the lead-in is found. +; We are about to load data so change the border colours. + + LD A,C ; fetch long-term mask from C + XOR $03 ; and make blue/yellow. + + LD C,A ; store the new long-term byte. + + LD H,$00 ; set up parity byte as zero. + LD B,$B0 ; timing. + JR L05C8 ; forward to LD-MARKER + ; the loop mid entry point with the alternate + ; zero flag reset to indicate first byte + ; is discarded. + +; -------------- +; the loading loop loads each byte and is entered at the mid point. + +;; LD-LOOP +L05A9: EX AF,AF' ; restore entry flags and type in A. + JR NZ,L05B3 ; forward to LD-FLAG if awaiting initial flag + ; which is to be discarded. + + JR NC,L05BD ; forward to LD-VERIFY if not to be loaded. + + LD (IX+$00),L ; place loaded byte at memory location. + JR L05C2 ; forward to LD-NEXT + +; --- + +;; LD-FLAG +L05B3: RL C ; preserve carry (verify) flag in long-term + ; state byte. Bit 7 can be lost. + + XOR L ; compare type in A with first byte in L. + RET NZ ; return if no match e.g. CODE vs DATA. + +; continue when data type matches. + + LD A,C ; fetch byte with stored carry + RRA ; rotate it to carry flag again + LD C,A ; restore long-term port state. + + INC DE ; increment length ?? + JR L05C4 ; forward to LD-DEC. + ; but why not to location after ? + +; --- +; for verification the byte read from tape is compared with that in memory. + +;; LD-VERIFY +L05BD: LD A,(IX+$00) ; fetch byte from memory. + XOR L ; compare with that on tape + RET NZ ; return if not zero. + +;; LD-NEXT +L05C2: INC IX ; increment byte pointer. + +;; LD-DEC +L05C4: DEC DE ; decrement length. + EX AF,AF' ; store the flags. + LD B,$B2 ; timing. + +; when starting to read 8 bits the receiving byte is marked with bit at right. +; when this is rotated out again then 8 bits have been read. + +;; LD-MARKER +L05C8: LD L,$01 ; initialize as %00000001 + +;; LD-8-BITS +L05CA: CALL L05E3 ; routine LD-EDGE-2 increments B relative to + ; gap between 2 edges. + RET NC ; return with time-out. + + LD A,$CB ; the comparison byte. + CP B ; compare to incremented value of B. + ; if B is higher then bit on tape was set. + ; if <= then bit on tape is reset. + + RL L ; rotate the carry bit into L. + + LD B,$B0 ; reset the B timer byte. + JP NC,L05CA ; JUMP back to LD-8-BITS + +; when carry set then marker bit has been passed out and byte is complete. + + LD A,H ; fetch the running parity byte. + XOR L ; include the new byte. + LD H,A ; and store back in parity register. + + LD A,D ; check length of + OR E ; expected bytes. + JR NZ,L05A9 ; back to LD-LOOP + ; while there are more. + +; when all bytes loaded then parity byte should be zero. + + LD A,H ; fetch parity byte. + CP $01 ; set carry if zero. + RET ; return + ; in no carry then error as checksum disagrees. + +; ------------------------- +; Check signal being loaded +; ------------------------- +; An edge is a transition from one mic state to another. +; More specifically a change in bit 6 of value input from port $FE. +; Graphically it is a change of border colour, say, blue to yellow. +; The first entry point looks for two adjacent edges. The second entry point +; is used to find a single edge. +; The B register holds a count, up to 256, within which the edge (or edges) +; must be found. The gap between two edges will be more for a '1' than a '0' +; so the value of B denotes the state of the bit (two edges) read from tape. + +; -> + +;; LD-EDGE-2 +L05E3: CALL L05E7 ; call routine LD-EDGE-1 below. + RET NC ; return if space pressed or time-out. + ; else continue and look for another adjacent + ; edge which together represent a bit on the + ; tape. + +; -> +; this entry point is used to find a single edge from above but also +; when detecting a read-in signal on the tape. + +;; LD-EDGE-1 +L05E7: LD A,$16 ; a delay value of twenty two. + +;; LD-DELAY +L05E9: DEC A ; decrement counter + JR NZ,L05E9 ; loop back to LD-DELAY 22 times. + + AND A ; clear carry. + +;; LD-SAMPLE +L05ED: INC B ; increment the time-out counter. + RET Z ; return with failure when $FF passed. + + LD A,$7F ; prepare to read keyboard and EAR port + IN A,($FE) ; row $7FFE. bit 6 is EAR, bit 0 is SPACE key. + RRA ; test outer key the space. (bit 6 moves to 5) + RET NC ; return if space pressed. >>> + + XOR C ; compare with initial long-term state. + AND $20 ; isolate bit 5 + JR Z,L05ED ; back to LD-SAMPLE if no edge. + +; but an edge, a transition of the EAR bit, has been found so switch the +; long-term comparison byte containing both border colour and EAR bit. + + LD A,C ; fetch comparison value. + CPL ; switch the bits + LD C,A ; and put back in C for long-term. + + AND $07 ; isolate new colour bits. + OR $08 ; set bit 3 - MIC off. + OUT ($FE),A ; send to port to effect change of colour. + + SCF ; set carry flag signaling edge found within + ; time allowed. + RET ; return. + +; --------------------------------- +; Entry point for all tape commands +; --------------------------------- +; This is the single entry point for the four tape commands. +; The routine first determines in what context it has been called by examining +; the low byte of the Syntax table entry which was stored in T_ADDR. +; Subtracting $EO (the present arrangement) gives a value of +; $00 - SAVE +; $01 - LOAD +; $02 - VERIFY +; $03 - MERGE +; As with all commands the address STMT-RET is on the stack. + +;; SAVE-ETC +L0605: POP AF ; discard address STMT-RET. + LD A,($5C74) ; fetch T_ADDR + +; Now reduce the low byte of the Syntax table entry to give command. +; Note. For ZASM use SUB $E0 as next instruction. + +L0609: SUB low (L1ADF + 1) ; subtract the known offset. + ; ( is SUB $E0 in standard ROM ) + + LD ($5C74),A ; and put back in T_ADDR as 0,1,2, or 3 + ; for future reference. + + CALL L1C8C ; routine EXPT-EXP checks that a string + ; expression follows and stacks the + ; parameters in run-time. + + CALL L2530 ; routine SYNTAX-Z + JR Z,L0652 ; forward to SA-DATA if checking syntax. + + LD BC,$0011 ; presume seventeen bytes for a header. + LD A,($5C74) ; fetch command from T_ADDR. + AND A ; test for zero - SAVE. + JR Z,L0621 ; forward to SA-SPACE if so. + + LD C,$22 ; else double length to thirty four. + +;; SA-SPACE +L0621: RST 30H ; BC-SPACES creates 17/34 bytes in workspace. + + PUSH DE ; transfer the start of new space to + POP IX ; the available index register. + +; ten spaces are required for the default filename but it is simpler to +; overwrite the first file-type indicator byte as well. + + LD B,$0B ; set counter to eleven. + LD A,$20 ; prepare a space. + +;; SA-BLANK +L0629: LD (DE),A ; set workspace location to space. + INC DE ; next location. + DJNZ L0629 ; loop back to SA-BLANK till all eleven done. + + LD (IX+$01),$FF ; set first byte of ten character filename + ; to $FF as a default to signal null string. + + CALL L2BF1 ; routine STK-FETCH fetches the filename + ; parameters from the calculator stack. + ; length of string in BC. + ; start of string in DE. + + LD HL,$FFF6 ; prepare the value minus ten. + DEC BC ; decrement length. + ; ten becomes nine, zero becomes $FFFF. + ADD HL,BC ; trial addition. + INC BC ; restore true length. + JR NC,L064B ; forward to SA-NAME if length is one to ten. + +; the filename is more than ten characters in length or the null string. + + LD A,($5C74) ; fetch command from T_ADDR. + AND A ; test for zero - SAVE. + JR NZ,L0644 ; forward to SA-NULL if not the SAVE command. + +; but no more than ten characters are allowed for SAVE. +; The first ten characters of any other command parameter are acceptable. +; Weird, but necessary, if saving to sectors. +; Note. the golden rule that there are no restriction on anything is broken. + +;; REPORT-Fa +L0642: RST 08H ; ERROR-1 + DEFB $0E ; Error Report: Invalid file name + +; continue with LOAD, MERGE, VERIFY and also SAVE within ten character limit. + +;; SA-NULL +L0644: LD A,B ; test length of filename + OR C ; for zero. + JR Z,L0652 ; forward to SA-DATA if so using the 255 + ; indicator followed by spaces. + + LD BC,$000A ; else trim length to ten. + +; other paths rejoin here with BC holding length in range 1 - 10. + +;; SA-NAME +L064B: PUSH IX ; push start of file descriptor. + POP HL ; and pop into HL. + + INC HL ; HL now addresses first byte of filename. + EX DE,HL ; transfer destination address to DE, start + ; of string in command to HL. + LDIR ; copy up to ten bytes + ; if less than ten then trailing spaces follow. + +; the case for the null string rejoins here. + +;; SA-DATA +L0652: RST 18H ; GET-CHAR + CP $E4 ; is character after filename the token 'DATA' ? + JR NZ,L06A0 ; forward to SA-SCR$ to consider SCREEN$ if + ; not. + +; continue to consider DATA. + + LD A,($5C74) ; fetch command from T_ADDR + CP $03 ; is it 'VERIFY' ? + JP Z,L1C8A ; jump forward to REPORT-C if so. + ; 'Nonsense in BASIC' + ; VERIFY "d" DATA is not allowed. + +; continue with SAVE, LOAD, MERGE of DATA. + + RST 20H ; NEXT-CHAR + CALL L28B2 ; routine LOOK-VARS searches variables area + ; returning with carry reset if found or + ; checking syntax. + SET 7,C ; this converts a simple string to a + ; string array. The test for an array or string + ; comes later. + JR NC,L0672 ; forward to SA-V-OLD if variable found. + + LD HL,$0000 ; set destination to zero as not fixed. + LD A,($5C74) ; fetch command from T_ADDR + DEC A ; test for 1 - LOAD + JR Z,L0685 ; forward to SA-V-NEW with LOAD DATA. + ; to load a new array. + +; otherwise the variable was not found in run-time with SAVE/MERGE. + +;; REPORT-2a +L0670: RST 08H ; ERROR-1 + DEFB $01 ; Error Report: Variable not found + +; continue with SAVE/LOAD DATA + +;; SA-V-OLD +L0672: JP NZ,L1C8A ; to REPORT-C if not an array variable. + ; or erroneously a simple string. + ; 'Nonsense in BASIC' + + + CALL L2530 ; routine SYNTAX-Z + JR Z,L0692 ; forward to SA-DATA-1 if checking syntax. + + INC HL ; step past single character variable name. + LD A,(HL) ; fetch low byte of length. + LD (IX+$0B),A ; place in descriptor. + INC HL ; point to high byte. + LD A,(HL) ; and transfer that + LD (IX+$0C),A ; to descriptor. + INC HL ; increase pointer within variable. + +;; SA-V-NEW +L0685: LD (IX+$0E),C ; place character array name in header. + LD A,$01 ; default to type numeric. + BIT 6,C ; test result from look-vars. + JR Z,L068F ; forward to SA-V-TYPE if numeric. + + INC A ; set type to 2 - string array. + +;; SA-V-TYPE +L068F: LD (IX+$00),A ; place type 0, 1 or 2 in descriptor. + +;; SA-DATA-1 +L0692: EX DE,HL ; save var pointer in DE + + RST 20H ; NEXT-CHAR + CP $29 ; is character ')' ? + JR NZ,L0672 ; back if not to SA-V-OLD to report + ; 'Nonsense in BASIC' + + RST 20H ; NEXT-CHAR advances character address. + CALL L1BEE ; routine CHECK-END errors if not end of + ; the statement. + + EX DE,HL ; bring back variables data pointer. + JP L075A ; jump forward to SA-ALL + +; --- +; the branch was here to consider a 'SCREEN$', the display file. + +;; SA-SCR$ +L06A0: CP $AA ; is character the token 'SCREEN$' ? + JR NZ,L06C3 ; forward to SA-CODE if not. + + LD A,($5C74) ; fetch command from T_ADDR + CP $03 ; is it MERGE ? + JP Z,L1C8A ; jump to REPORT-C if so. + ; 'Nonsense in BASIC' + +; continue with SAVE/LOAD/VERIFY SCREEN$. + + RST 20H ; NEXT-CHAR + CALL L1BEE ; routine CHECK-END errors if not at end of + ; statement. + +; continue in runtime. + + LD (IX+$0B),$00 ; set descriptor length + LD (IX+$0C),$1B ; to $1b00 to include bitmaps and attributes. + + LD HL,$4000 ; set start to display file start. + LD (IX+$0D),L ; place start in + LD (IX+$0E),H ; the descriptor. + JR L0710 ; forward to SA-TYPE-3 + +; --- +; the branch was here to consider CODE. + +;; SA-CODE +L06C3: CP $AF ; is character the token 'CODE' ? + JR NZ,L0716 ; forward if not to SA-LINE to consider an + ; auto-started BASIC program. + + LD A,($5C74) ; fetch command from T_ADDR + CP $03 ; is it MERGE ? + JP Z,L1C8A ; jump forward to REPORT-C if so. + ; 'Nonsense in BASIC' + + + RST 20H ; NEXT-CHAR advances character address. + CALL L2048 ; routine PR-ST-END checks if a carriage + ; return or ':' follows. + JR NZ,L06E1 ; forward to SA-CODE-1 if there are parameters. + + LD A,($5C74) ; else fetch the command from T_ADDR. + AND A ; test for zero - SAVE without a specification. + JP Z,L1C8A ; jump to REPORT-C if so. + ; 'Nonsense in BASIC' + +; for LOAD/VERIFY put zero on stack to signify handle at location saved from. + + CALL L1CE6 ; routine USE-ZERO + JR L06F0 ; forward to SA-CODE-2 + +; --- +; if there are more characters after CODE expect start and possibly length. + +;; SA-CODE-1 +L06E1: CALL L1C82 ; routine EXPT-1NUM checks for numeric + ; expression and stacks it in run-time. + + RST 18H ; GET-CHAR + CP $2C ; does a comma follow ? + JR Z,L06F5 ; forward if so to SA-CODE-3 + +; else allow saved code to be loaded to a specified address. + + LD A,($5C74) ; fetch command from T_ADDR. + AND A ; is the command SAVE which requires length ? + JP Z,L1C8A ; jump to REPORT-C if so. + ; 'Nonsense in BASIC' + +; the command LOAD code may rejoin here with zero stacked as start. + +;; SA-CODE-2 +L06F0: CALL L1CE6 ; routine USE-ZERO stacks zero for length. + JR L06F9 ; forward to SA-CODE-4 + +; --- +; the branch was here with SAVE CODE start, + +;; SA-CODE-3 +L06F5: RST 20H ; NEXT-CHAR advances character address. + CALL L1C82 ; routine EXPT-1NUM checks for expression + ; and stacks in run-time. + +; paths converge here and nothing must follow. + +;; SA-CODE-4 +L06F9: CALL L1BEE ; routine CHECK-END errors with extraneous + ; characters and quits if checking syntax. + +; in run-time there are two 16-bit parameters on the calculator stack. + + CALL L1E99 ; routine FIND-INT2 gets length. + LD (IX+$0B),C ; place length + LD (IX+$0C),B ; in descriptor. + CALL L1E99 ; routine FIND-INT2 gets start. + LD (IX+$0D),C ; place start + LD (IX+$0E),B ; in descriptor. + LD H,B ; transfer the + LD L,C ; start to HL also. + +;; SA-TYPE-3 +L0710: LD (IX+$00),$03 ; place type 3 - code in descriptor. + JR L075A ; forward to SA-ALL. + +; --- +; the branch was here with BASIC to consider an optional auto-start line +; number. + +;; SA-LINE +L0716: CP $CA ; is character the token 'LINE' ? + JR Z,L0723 ; forward to SA-LINE-1 if so. + +; else all possibilities have been considered and nothing must follow. + + CALL L1BEE ; routine CHECK-END + +; continue in run-time to save BASIC without auto-start. + + LD (IX+$0E),$80 ; place high line number in descriptor to + ; disable auto-start. + JR L073A ; forward to SA-TYPE-0 to save program. + +; --- +; the branch was here to consider auto-start. + +;; SA-LINE-1 +L0723: LD A,($5C74) ; fetch command from T_ADDR + AND A ; test for SAVE. + JP NZ,L1C8A ; jump forward to REPORT-C with anything else. + ; 'Nonsense in BASIC' + +; + + RST 20H ; NEXT-CHAR + CALL L1C82 ; routine EXPT-1NUM checks for numeric + ; expression and stacks in run-time. + CALL L1BEE ; routine CHECK-END quits if syntax path. + CALL L1E99 ; routine FIND-INT2 fetches the numeric + ; expression. + LD (IX+$0D),C ; place the auto-start + LD (IX+$0E),B ; line number in the descriptor. + +; Note. this isn't checked, but is subsequently handled by the system. +; If the user typed 40000 instead of 4000 then it won't auto-start +; at line 4000, or indeed, at all. + +; continue to save program and any variables. + +;; SA-TYPE-0 +L073A: LD (IX+$00),$00 ; place type zero - program in descriptor. + LD HL,($5C59) ; fetch E_LINE to HL. + LD DE,($5C53) ; fetch PROG to DE. + SCF ; set carry flag to calculate from end of + ; variables E_LINE -1. + SBC HL,DE ; subtract to give total length. + + LD (IX+$0B),L ; place total length + LD (IX+$0C),H ; in descriptor. + LD HL,($5C4B) ; load HL from system variable VARS + SBC HL,DE ; subtract to give program length. + LD (IX+$0F),L ; place length of program + LD (IX+$10),H ; in the descriptor. + EX DE,HL ; start to HL, length to DE. + +;; SA-ALL +L075A: LD A,($5C74) ; fetch command from T_ADDR + AND A ; test for zero - SAVE. + JP Z,L0970 ; jump forward to SA-CONTRL with SAVE -> + +; --- +; continue with LOAD, MERGE and VERIFY. + + PUSH HL ; save start. + LD BC,$0011 ; prepare to add seventeen + ADD IX,BC ; to point IX at second descriptor. + +;; LD-LOOK-H +L0767: PUSH IX ; save IX + LD DE,$0011 ; seventeen bytes + XOR A ; reset zero flag + SCF ; set carry flag + CALL L0556 ; routine LD-BYTES loads a header from tape + ; to second descriptor. + POP IX ; restore IX. + JR NC,L0767 ; loop back to LD-LOOK-H until header found. + + LD A,$FE ; select system channel 'S' + CALL L1601 ; routine CHAN-OPEN opens it. + + LD (IY+$52),$03 ; set SCR_CT to 3 lines. + + LD C,$80 ; C has bit 7 set to indicate type mismatch as + ; a default startpoint. + + LD A,(IX+$00) ; fetch loaded header type to A + CP (IX-$11) ; compare with expected type. + JR NZ,L078A ; forward to LD-TYPE with mis-match. + + LD C,$F6 ; set C to minus ten - will count characters + ; up to zero. + +;; LD-TYPE +L078A: CP $04 ; check if type in acceptable range 0 - 3. + JR NC,L0767 ; back to LD-LOOK-H with 4 and over. + +; else A indicates type 0-3. + + LD DE,L09C0 ; address base of last 4 tape messages + PUSH BC ; save BC + CALL L0C0A ; routine PO-MSG outputs relevant message. + ; Note. all messages have a leading newline. + POP BC ; restore BC + + PUSH IX ; transfer IX, + POP DE ; the 2nd descriptor, to DE. + LD HL,$FFF0 ; prepare minus seventeen. + ADD HL,DE ; add to point HL to 1st descriptor. + LD B,$0A ; the count will be ten characters for the + ; filename. + + LD A,(HL) ; fetch first character and test for + INC A ; value 255. + JR NZ,L07A6 ; forward to LD-NAME if not the wildcard. + +; but if it is the wildcard, then add ten to C which is minus ten for a type +; match or -128 for a type mismatch. Although characters have to be counted +; bit 7 of C will not alter from state set here. + + LD A,C ; transfer $F6 or $80 to A + ADD A,B ; add $0A + LD C,A ; place result, zero or -118, in C. + +; At this point we have either a type mismatch, a wildcard match or ten +; characters to be counted. The characters must be shown on the screen. + +;; LD-NAME +L07A6: INC DE ; address next input character + LD A,(DE) ; fetch character + CP (HL) ; compare to expected + INC HL ; address next expected character + JR NZ,L07AD ; forward to LD-CH-PR with mismatch + + INC C ; increment matched character count + +;; LD-CH-PR +L07AD: RST 10H ; PRINT-A prints character + DJNZ L07A6 ; loop back to LD-NAME for ten characters. + +; if ten characters matched and the types previously matched then C will +; now hold zero. + + BIT 7,C ; test if all matched + JR NZ,L0767 ; back to LD-LOOK-H if not + +; else print a terminal carriage return. + + LD A,$0D ; prepare carriage return. + RST 10H ; PRINT-A outputs it. + +; The various control routines for LOAD, VERIFY and MERGE are executed +; during the one-second gap following the header on tape. + + POP HL ; restore xx + LD A,(IX+$00) ; fetch incoming type + CP $03 ; compare with CODE + JR Z,L07CB ; forward to VR-CONTROL if it is CODE. + +; type is a program or an array. + + LD A,($5C74) ; fetch command from T_ADDR + DEC A ; was it LOAD ? + JP Z,L0808 ; JUMP forward to LD-CONTRL if so to + ; load BASIC or variables. + + CP $02 ; was command MERGE ? + JP Z,L08B6 ; jump forward to ME-CONTRL if so. + +; else continue into VERIFY control routine to verify. + +; --------------------- +; Handle VERIFY control +; --------------------- +; There are two branches to this routine. +; 1) From above to verify a program or array +; 2) from earlier with no carry to load or verify code. + +;; VR-CONTROL +L07CB: PUSH HL ; save pointer to data. + LD L,(IX-$06) ; fetch length of old data + LD H,(IX-$05) ; to HL. + LD E,(IX+$0B) ; fetch length of new data + LD D,(IX+$0C) ; to DE. + LD A,H ; check length of old + OR L ; for zero. + JR Z,L07E9 ; forward to VR-CONT-1 if length unspecified + ; e.g LOAD "x" CODE + +; as opposed to, say, LOAD 'x' CODE 32768,300. + + SBC HL,DE ; subtract the two lengths. + JR C,L0806 ; forward to REPORT-R if the length on tape is + ; larger than that specified in command. + ; 'Tape loading error' + + JR Z,L07E9 ; forward to VR-CONT-1 if lengths match. + +; a length on tape shorter than expected is not allowed for CODE + + LD A,(IX+$00) ; else fetch type from tape. + CP $03 ; is it CODE ? + JR NZ,L0806 ; forward to REPORT-R if so + ; 'Tape loading error' + +;; VR-CONT-1 +L07E9: POP HL ; pop pointer to data + LD A,H ; test for zero + OR L ; e.g. LOAD 'x' CODE + JR NZ,L07F4 ; forward to VR-CONT-2 if destination specified. + + LD L,(IX+$0D) ; else use the destination in the header + LD H,(IX+$0E) ; and load code at address saved from. + +;; VR-CONT-2 +L07F4: PUSH HL ; push pointer to start of data block. + POP IX ; transfer to IX. + LD A,($5C74) ; fetch reduced command from T_ADDR + CP $02 ; is it VERIFY ? + SCF ; prepare a set carry flag + JR NZ,L0800 ; skip to VR-CONT-3 if not + + AND A ; clear carry flag for VERIFY so that + ; data is not loaded. + +;; VR-CONT-3 +L0800: LD A,$FF ; signal data block to be loaded + +; ----------------- +; Load a data block +; ----------------- +; This routine is called from 3 places other than above to load a data block. +; In all cases the accumulator is first set to $FF so the routine could be +; called at the previous instruction. + +;; LD-BLOCK +L0802: CALL L0556 ; routine LD-BYTES + RET C ; return if successful. + + +;; REPORT-R +L0806: RST 08H ; ERROR-1 + DEFB $1A ; Error Report: Tape loading error + +; ------------------- +; Handle LOAD control +; ------------------- +; This branch is taken when the command is LOAD with type 0, 1 or 2. + +;; LD-CONTRL +L0808: LD E,(IX+$0B) ; fetch length of found data block + LD D,(IX+$0C) ; from 2nd descriptor. + PUSH HL ; save destination + LD A,H ; test for zero + OR L ; + JR NZ,L0819 ; forward if not to LD-CONT-1 + + INC DE ; increase length + INC DE ; for letter name + INC DE ; and 16-bit length + EX DE,HL ; length to HL, + JR L0825 ; forward to LD-CONT-2 + +; --- + +;; LD-CONT-1 +L0819: LD L,(IX-$06) ; fetch length from + LD H,(IX-$05) ; the first header. + EX DE,HL ; + SCF ; set carry flag + SBC HL,DE ; + JR C,L082E ; to LD-DATA + +;; LD-CONT-2 +L0825: LD DE,$0005 ; allow overhead of five bytes. + ADD HL,DE ; add in the difference in data lengths. + LD B,H ; transfer to + LD C,L ; the BC register pair + CALL L1F05 ; routine TEST-ROOM fails if not enough room. + +;; LD-DATA +L082E: POP HL ; pop destination + LD A,(IX+$00) ; fetch type 0, 1 or 2. + AND A ; test for program and variables. + JR Z,L0873 ; forward if so to LD-PROG + +; the type is a numeric or string array. + + LD A,H ; test the destination for zero + OR L ; indicating variable does not already exist. + JR Z,L084C ; forward if so to LD-DATA-1 + +; else the destination is the first dimension within the array structure + + DEC HL ; address high byte of total length + LD B,(HL) ; transfer to B. + DEC HL ; address low byte of total length. + LD C,(HL) ; transfer to C. + DEC HL ; point to letter of variable. + INC BC ; adjust length to + INC BC ; include these + INC BC ; three bytes also. + LD ($5C5F),IX ; save header pointer in X_PTR. + CALL L19E8 ; routine RECLAIM-2 reclaims the old variable + ; sliding workspace including the two headers + ; downwards. + LD IX,($5C5F) ; reload IX from X_PTR which will have been + ; adjusted down by POINTERS routine. + +;; LD-DATA-1 +L084C: LD HL,($5C59) ; address E_LINE + DEC HL ; now point to the $80 variables end-marker. + LD C,(IX+$0B) ; fetch new data length + LD B,(IX+$0C) ; from 2nd header. + PUSH BC ; * save it. + INC BC ; adjust the + INC BC ; length to include + INC BC ; letter name and total length. + LD A,(IX-$03) ; fetch letter name from old header. + PUSH AF ; preserve accumulator though not corrupted. + + CALL L1655 ; routine MAKE-ROOM creates space for variable + ; sliding workspace up. IX no longer addresses + ; anywhere meaningful. + INC HL ; point to first new location. + + POP AF ; fetch back the letter name. + LD (HL),A ; place in first new location. + POP DE ; * pop the data length. + INC HL ; address 2nd location + LD (HL),E ; store low byte of length. + INC HL ; address next. + LD (HL),D ; store high byte. + INC HL ; address start of data. + PUSH HL ; transfer address + POP IX ; to IX register pair. + SCF ; set carry flag indicating load not verify. + LD A,$FF ; signal data not header. + JP L0802 ; JUMP back to LD-BLOCK + +; ----------------- +; the branch is here when a program as opposed to an array is to be loaded. + +;; LD-PROG +L0873: EX DE,HL ; transfer dest to DE. + LD HL,($5C59) ; address E_LINE + DEC HL ; now variables end-marker. + LD ($5C5F),IX ; place the IX header pointer in X_PTR + LD C,(IX+$0B) ; get new length + LD B,(IX+$0C) ; from 2nd header + PUSH BC ; and save it. + + CALL L19E5 ; routine RECLAIM-1 reclaims program and vars. + ; adjusting X-PTR. + + POP BC ; restore new length. + PUSH HL ; * save start + PUSH BC ; ** and length. + + CALL L1655 ; routine MAKE-ROOM creates the space. + + LD IX,($5C5F) ; reload IX from adjusted X_PTR + INC HL ; point to start of new area. + LD C,(IX+$0F) ; fetch length of BASIC on tape + LD B,(IX+$10) ; from 2nd descriptor + ADD HL,BC ; add to address the start of variables. + LD ($5C4B),HL ; set system variable VARS + + LD H,(IX+$0E) ; fetch high byte of autostart line number. + LD A,H ; transfer to A + AND $C0 ; test if greater than $3F. + JR NZ,L08AD ; forward to LD-PROG-1 if so with no autostart. + + LD L,(IX+$0D) ; else fetch the low byte. + LD ($5C42),HL ; set sytem variable to line number NEWPPC + LD (IY+$0A),$00 ; set statement NSPPC to zero. + +;; LD-PROG-1 +L08AD: POP DE ; ** pop the length + POP IX ; * and start. + SCF ; set carry flag + LD A,$FF ; signal data as opposed to a header. + JP L0802 ; jump back to LD-BLOCK + +; -------------------- +; Handle MERGE control +; -------------------- +; the branch was here to merge a program and its variables or an array. +; + +;; ME-CONTRL +L08B6: LD C,(IX+$0B) ; fetch length + LD B,(IX+$0C) ; of data block on tape. + PUSH BC ; save it. + INC BC ; one for the pot. + + RST 30H ; BC-SPACES creates room in workspace. + ; HL addresses last new location. + LD (HL),$80 ; place end-marker at end. + EX DE,HL ; transfer first location to HL. + POP DE ; restore length to DE. + PUSH HL ; save start. + + PUSH HL ; and transfer it + POP IX ; to IX register. + SCF ; set carry flag to load data on tape. + LD A,$FF ; signal data not a header. + CALL L0802 ; routine LD-BLOCK loads to workspace. + POP HL ; restore first location in workspace to HL. +X08CE LD DE,($5C53) ; set DE from system variable PROG. + +; now enter a loop to merge the data block in workspace with the program and +; variables. + +;; ME-NEW-LP +L08D2: LD A,(HL) ; fetch next byte from workspace. + AND $C0 ; compare with $3F. + JR NZ,L08F0 ; forward to ME-VAR-LP if a variable or + ; end-marker. + +; continue when HL addresses a BASIC line number. + +;; ME-OLD-LP +L08D7: LD A,(DE) ; fetch high byte from program area. + INC DE ; bump prog address. + CP (HL) ; compare with that in workspace. + INC HL ; bump workspace address. + JR NZ,L08DF ; forward to ME-OLD-L1 if high bytes don't match + + LD A,(DE) ; fetch the low byte of program line number. + CP (HL) ; compare with that in workspace. + +;; ME-OLD-L1 +L08DF: DEC DE ; point to start of + DEC HL ; respective lines again. + JR NC,L08EB ; forward to ME-NEW-L2 if line number in + ; workspace is less than or equal to current + ; program line as has to be added to program. + + PUSH HL ; else save workspace pointer. + EX DE,HL ; transfer prog pointer to HL + CALL L19B8 ; routine NEXT-ONE finds next line in DE. + POP HL ; restore workspace pointer + JR L08D7 ; back to ME-OLD-LP until destination position + ; in program area found. + +; --- +; the branch was here with an insertion or replacement point. + +;; ME-NEW-L2 +L08EB: CALL L092C ; routine ME-ENTER enters the line + JR L08D2 ; loop back to ME-NEW-LP. + +; --- +; the branch was here when the location in workspace held a variable. + +;; ME-VAR-LP +L08F0: LD A,(HL) ; fetch first byte of workspace variable. + LD C,A ; copy to C also. + CP $80 ; is it the end-marker ? + RET Z ; return if so as complete. >>>>> + + PUSH HL ; save workspace area pointer. + LD HL,($5C4B) ; load HL with VARS - start of variables area. + +;; ME-OLD-VP +L08F9: LD A,(HL) ; fetch first byte. + CP $80 ; is it the end-marker ? + JR Z,L0923 ; forward if so to ME-VAR-L2 to add + ; variable at end of variables area. + + CP C ; compare with variable in workspace area. + JR Z,L0909 ; forward to ME-OLD-V2 if a match to replace. + +; else entire variables area has to be searched. + +;; ME-OLD-V1 +L0901: PUSH BC ; save character in C. + CALL L19B8 ; routine NEXT-ONE gets following variable + ; address in DE. + POP BC ; restore character in C + EX DE,HL ; transfer next address to HL. + JR L08F9 ; loop back to ME-OLD-VP + +; --- +; the branch was here when first characters of name matched. + +;; ME-OLD-V2 +L0909: AND $E0 ; keep bits 11100000 + CP $A0 ; compare 10100000 - a long-named variable. + + JR NZ,L0921 ; forward to ME-VAR-L1 if just one-character. + +; but long-named variables have to be matched character by character. + + POP DE ; fetch workspace 1st character pointer + PUSH DE ; and save it on the stack again. + PUSH HL ; save variables area pointer on stack. + +;; ME-OLD-V3 +L0912: INC HL ; address next character in vars area. + INC DE ; address next character in workspace area. + LD A,(DE) ; fetch workspace character. + CP (HL) ; compare to variables character. + JR NZ,L091E ; forward to ME-OLD-V4 with a mismatch. + + RLA ; test if the terminal inverted character. + JR NC,L0912 ; loop back to ME-OLD-V3 if more to test. + +; otherwise the long name matches in its entirety. + + POP HL ; restore pointer to first character of variable + JR L0921 ; forward to ME-VAR-L1 + +; --- +; the branch is here when two characters don't match + +;; ME-OLD-V4 +L091E: POP HL ; restore the prog/vars pointer. + JR L0901 ; back to ME-OLD-V1 to resume search. + +; --- +; branch here when variable is to replace an existing one + +;; ME-VAR-L1 +L0921: LD A,$FF ; indicate a replacement. + +; this entry point is when A holds $80 indicating a new variable. + +;; ME-VAR-L2 +L0923: POP DE ; pop workspace pointer. + EX DE,HL ; now make HL workspace pointer, DE vars pointer + INC A ; zero flag set if replacement. + SCF ; set carry flag indicating a variable not a + ; program line. + CALL L092C ; routine ME-ENTER copies variable in. + JR L08F0 ; loop back to ME-VAR-LP + +; ------------------------ +; Merge a Line or Variable +; ------------------------ +; A BASIC line or variable is inserted at the current point. If the line numbers +; or variable names match (zero flag set) then a replacement takes place. + +;; ME-ENTER +L092C: JR NZ,L093E ; forward to ME-ENT-1 for insertion only. + +; but the program line or variable matches so old one is reclaimed. + + EX AF,AF' ; save flag?? + LD ($5C5F),HL ; preserve workspace pointer in dynamic X_PTR + EX DE,HL ; transfer program dest pointer to HL. + CALL L19B8 ; routine NEXT-ONE finds following location + ; in program or variables area. + CALL L19E8 ; routine RECLAIM-2 reclaims the space between. + EX DE,HL ; transfer program dest pointer back to DE. + LD HL,($5C5F) ; fetch adjusted workspace pointer from X_PTR + EX AF,AF' ; restore flags. + +; now the new line or variable is entered. + +;; ME-ENT-1 +L093E: EX AF,AF' ; save or re-save flags. + PUSH DE ; save dest pointer in prog/vars area. + CALL L19B8 ; routine NEXT-ONE finds next in workspace. + ; gets next in DE, difference in BC. + ; prev addr in HL + LD ($5C5F),HL ; store pointer in X_PTR + LD HL,($5C53) ; load HL from system variable PROG + EX (SP),HL ; swap with prog/vars pointer on stack. + PUSH BC ; ** save length of new program line/variable. + EX AF,AF' ; fetch flags back. + JR C,L0955 ; skip to ME-ENT-2 if variable + + DEC HL ; address location before pointer + CALL L1655 ; routine MAKE-ROOM creates room for BASIC line + INC HL ; address next. + JR L0958 ; forward to ME-ENT-3 + +; --- + +;; ME-ENT-2 +L0955: CALL L1655 ; routine MAKE-ROOM creates room for variable. + +;; ME-ENT-3 +L0958: INC HL ; address next? + + POP BC ; ** pop length + POP DE ; * pop value for PROG which may have been + ; altered by POINTERS if first line. + LD ($5C53),DE ; set PROG to original value. + LD DE,($5C5F) ; fetch adjusted workspace pointer from X_PTR + PUSH BC ; save length + PUSH DE ; and workspace pointer + EX DE,HL ; make workspace pointer source, prog/vars + ; pointer the destination + LDIR ; copy bytes of line or variable into new area. + POP HL ; restore workspace pointer. + POP BC ; restore length. + PUSH DE ; save new prog/vars pointer. + CALL L19E8 ; routine RECLAIM-2 reclaims the space used + ; by the line or variable in workspace block + ; as no longer required and space could be + ; useful for adding more lines. + POP DE ; restore the prog/vars pointer + RET ; return. + +; ------------------- +; Handle SAVE control +; ------------------- +; A branch from the main SAVE-ETC routine at SAVE-ALL. +; First the header data is saved. Then after a wait of 1 second +; the data itself is saved. +; HL points to start of data. +; IX points to start of descriptor. + +;; SA-CONTRL +L0970: PUSH HL ; save start of data + + LD A,$FD ; select system channel 'S' + CALL L1601 ; routine CHAN-OPEN + + XOR A ; clear to address table directly + LD DE,L09A1 ; address: tape-msgs + CALL L0C0A ; routine PO-MSG - + ; 'Start tape then press any key.' + + SET 5,(IY+$02) ; TV_FLAG - Signal lower screen requires + ; clearing + CALL L15D4 ; routine WAIT-KEY + + PUSH IX ; save pointer to descriptor. + LD DE,$0011 ; there are seventeen bytes. + XOR A ; signal a header. + CALL L04C2 ; routine SA-BYTES + + POP IX ; restore descriptor pointer. + + LD B,$32 ; wait for a second - 50 interrupts. + +;; SA-1-SEC +L0991: HALT ; wait for interrupt + DJNZ L0991 ; back to SA-1-SEC until pause complete. + + LD E,(IX+$0B) ; fetch length of bytes from the + LD D,(IX+$0C) ; descriptor. + + LD A,$FF ; signal data bytes. + + POP IX ; retrieve pointer to start + JP L04C2 ; jump back to SA-BYTES + + +; Arrangement of two headers in workspace. +; Originally IX addresses first location and only one header is required +; when saving. +; +; OLD NEW PROG DATA DATA CODE +; HEADER HEADER num chr NOTES. +; ------ ------ ---- ---- ---- ---- ----------------------------- +; IX-$11 IX+$00 0 1 2 3 Type. +; IX-$10 IX+$01 x x x x F ($FF if filename is null). +; IX-$0F IX+$02 x x x x i +; IX-$0E IX+$03 x x x x l +; IX-$0D IX+$04 x x x x e +; IX-$0C IX+$05 x x x x n +; IX-$0B IX+$06 x x x x a +; IX-$0A IX+$07 x x x x m +; IX-$09 IX+$08 x x x x e +; IX-$08 IX+$09 x x x x . +; IX-$07 IX+$0A x x x x (terminal spaces). +; IX-$06 IX+$0B lo lo lo lo Total +; IX-$05 IX+$0C hi hi hi hi Length of datablock. +; IX-$04 IX+$0D Auto - - Start Various +; IX-$03 IX+$0E Start a-z a-z addr ($80 if no autostart). +; IX-$02 IX+$0F lo - - - Length of Program +; IX-$01 IX+$10 hi - - - only i.e. without variables. +; + + +; ------------------------ +; Canned cassette messages +; ------------------------ +; The last-character-inverted Cassette messages. +; Starts with normal initial step-over byte. + +;; tape-msgs +L09A1: DEFB $80 + DEFM "Start tape, then press any key" +L09C0: DEFB '.'+$80 + DEFB $0D + DEFM "Program:" + DEFB ' '+$80 + DEFB $0D + DEFM "Number array:" + DEFB ' '+$80 + DEFB $0D + DEFM "Character array:" + DEFB ' '+$80 + DEFB $0D + DEFM "Bytes:" + DEFB ' '+$80 + + +;************************************************** +;** Part 5. SCREEN AND PRINTER HANDLING ROUTINES ** +;************************************************** + +; --------------------- +; General PRINT routine +; --------------------- +; This is the routine most often used by the RST 10 restart although the +; subroutine is on two occasions called directly when it is known that +; output will definitely be to the lower screen. + +;; PRINT-OUT +L09F4: CALL L0B03 ; routine PO-FETCH fetches print position + ; to HL register pair. + CP $20 ; is character a space or higher ? + JP NC,L0AD9 ; jump forward to PO-ABLE if so. + + CP $06 ; is character in range 00-05 ? + JR C,L0A69 ; to PO-QUEST to print '?' if so. + + CP $18 ; is character in range 24d - 31d ? + JR NC,L0A69 ; to PO-QUEST to also print '?' if so. + + LD HL,L0A11 - 6 ; address 0A0B - the base address of control + ; character table - where zero would be. + LD E,A ; control character 06 - 23d + LD D,$00 ; is transferred to DE. + + ADD HL,DE ; index into table. + + LD E,(HL) ; fetch the offset to routine. + ADD HL,DE ; add to make HL the address. + PUSH HL ; push the address. + JP L0B03 ; to PO-FETCH, as the screen/printer position + ; has been disturbed, and indirectly to + ; routine on stack. + +; ----------------------- +; Control character table +; ----------------------- +; For control characters in the range 6 - 23d the following table +; is indexed to provide an offset to the handling routine that +; follows the table. + +;; ctlchrtab +L0A11: DEFB L0A5F - $ ; 06d offset $4E to Address: PO-COMMA + DEFB L0A69 - $ ; 07d offset $57 to Address: PO-QUEST + DEFB L0A23 - $ ; 08d offset $10 to Address: PO-BACK-1 + DEFB L0A3D - $ ; 09d offset $29 to Address: PO-RIGHT + DEFB L0A69 - $ ; 10d offset $54 to Address: PO-QUEST + DEFB L0A69 - $ ; 11d offset $53 to Address: PO-QUEST + DEFB L0A69 - $ ; 12d offset $52 to Address: PO-QUEST + DEFB L0A4F - $ ; 13d offset $37 to Address: PO-ENTER + DEFB L0A69 - $ ; 14d offset $50 to Address: PO-QUEST + DEFB L0A69 - $ ; 15d offset $4F to Address: PO-QUEST + DEFB L0A7A - $ ; 16d offset $5F to Address: PO-1-OPER + DEFB L0A7A - $ ; 17d offset $5E to Address: PO-1-OPER + DEFB L0A7A - $ ; 18d offset $5D to Address: PO-1-OPER + DEFB L0A7A - $ ; 19d offset $5C to Address: PO-1-OPER + DEFB L0A7A - $ ; 20d offset $5B to Address: PO-1-OPER + DEFB L0A7A - $ ; 21d offset $5A to Address: PO-1-OPER + DEFB L0A75 - $ ; 22d offset $54 to Address: PO-2-OPER + DEFB L0A75 - $ ; 23d offset $53 to Address: PO-2-OPER + + +; ------------------- +; Cursor left routine +; ------------------- +; Backspace and up a line if that action is from the left of screen. +; For ZX printer backspace up to first column but not beyond. + +;; PO-BACK-1 +L0A23: INC C ; move left one column. + LD A,$22 ; value $21 is leftmost column. + CP C ; have we passed ? + JR NZ,L0A3A ; to PO-BACK-3 if not and store new position. + + BIT 1,(IY+$01) ; test FLAGS - is printer in use ? + JR NZ,L0A38 ; to PO-BACK-2 if so, as we are unable to + ; backspace from the leftmost position. + + + INC B ; move up one screen line + LD C,$02 ; the rightmost column position. + LD A,$18 ; Note. This should be $19 + ; credit. Dr. Frank O'Hara, 1982 + + CP B ; has position moved past top of screen ? + JR NZ,L0A3A ; to PO-BACK-3 if not and store new position. + + DEC B ; else back to $18. + +;; PO-BACK-2 +L0A38: LD C,$21 ; the leftmost column position. + +;; PO-BACK-3 +L0A3A: JP L0DD9 ; to CL-SET and PO-STORE to save new + ; position in system variables. + +; -------------------- +; Cursor right routine +; -------------------- +; This moves the print position to the right leaving a trail in the +; current background colour. +; "However the programmer has failed to store the new print position +; so CHR$ 9 will only work if the next print position is at a newly +; defined place. +; e.g. PRINT PAPER 2; CHR$ 9; AT 4,0; +; does work but is not very helpful" +; - Dr. Ian Logan, Understanding Your Spectrum, 1982. + +;; PO-RIGHT +L0A3D: LD A,($5C91) ; fetch P_FLAG value + PUSH AF ; and save it on stack. + + LD (IY+$57),$01 ; temporarily set P_FLAG 'OVER 1'. + LD A,$20 ; prepare a space. + CALL L0B65 ; routine PO-CHAR to print it. + ; Note. could be PO-ABLE which would update + ; the column position. + + POP AF ; restore the permanent flag. + LD ($5C91),A ; and restore system variable P_FLAG + + RET ; return without updating column position + +; ----------------------- +; Perform carriage return +; ----------------------- +; A carriage return is 'printed' to screen or printer buffer. + +;; PO-ENTER +L0A4F: BIT 1,(IY+$01) ; test FLAGS - is printer in use ? + JP NZ,L0ECD ; to COPY-BUFF if so, to flush buffer and reset + ; the print position. + + LD C,$21 ; the leftmost column position. + CALL L0C55 ; routine PO-SCR handles any scrolling required. + DEC B ; to next screen line. + JP L0DD9 ; jump forward to CL-SET to store new position. + +; ----------- +; Print comma +; ----------- +; The comma control character. The 32 column screen has two 16 character +; tabstops. The routine is only reached via the control character table. + +;; PO-COMMA +L0A5F: CALL L0B03 ; routine PO-FETCH - seems unnecessary. + + LD A,C ; the column position. $21-$01 + DEC A ; move right. $20-$00 + DEC A ; and again $1F-$00 or $FF if trailing + AND $10 ; will be $00 or $10. + JR L0AC3 ; forward to PO-FILL + +; ------------------- +; Print question mark +; ------------------- +; This routine prints a question mark which is commonly +; used to print an unassigned control character in range 0-31d. +; there are a surprising number yet to be assigned. + +;; PO-QUEST +L0A69: LD A,$3F ; prepare the character '?'. + JR L0AD9 ; forward to PO-ABLE. + +; -------------------------------- +; Control characters with operands +; -------------------------------- +; Certain control characters are followed by 1 or 2 operands. +; The entry points from control character table are PO-2-OPER and PO-1-OPER. +; The routines alter the output address of the current channel so that +; subsequent RST $10 instructions take the appropriate action +; before finally resetting the output address back to PRINT-OUT. + +;; PO-TV-2 +L0A6D: LD DE,L0A87 ; address: PO-CONT will be next output routine + LD ($5C0F),A ; store first operand in TVDATA-hi + JR L0A80 ; forward to PO-CHANGE >> + +; --- + +; -> This initial entry point deals with two operands - AT or TAB. + +;; PO-2-OPER +L0A75: LD DE,L0A6D ; address: PO-TV-2 will be next output routine + JR L0A7D ; forward to PO-TV-1 + +; --- + +; -> This initial entry point deals with one operand INK to OVER. + +;; PO-1-OPER +L0A7A: LD DE,L0A87 ; address: PO-CONT will be next output routine + +;; PO-TV-1 +L0A7D: LD ($5C0E),A ; store control code in TVDATA-lo + +;; PO-CHANGE +L0A80: LD HL,($5C51) ; use CURCHL to find current output channel. + LD (HL),E ; make it + INC HL ; the supplied + LD (HL),D ; address from DE. + RET ; return. + +; --- + +;; PO-CONT +L0A87: LD DE,L09F4 ; Address: PRINT-OUT + CALL L0A80 ; routine PO-CHANGE to restore normal channel. + LD HL,($5C0E) ; TVDATA gives control code and possible + ; subsequent character + LD D,A ; save current character + LD A,L ; the stored control code + CP $16 ; was it INK to OVER (1 operand) ? + JP C,L2211 ; to CO-TEMP-5 + + JR NZ,L0AC2 ; to PO-TAB if not 22d i.e. 23d TAB. + + ; else must have been 22d AT. + LD B,H ; line to H (0-23d) + LD C,D ; column to C (0-31d) + LD A,$1F ; the value 31d + SUB C ; reverse the column number. + JR C,L0AAC ; to PO-AT-ERR if C was greater than 31d. + + ADD A,$02 ; transform to system range $02-$21 + LD C,A ; and place in column register. + + BIT 1,(IY+$01) ; test FLAGS - is printer in use ? + JR NZ,L0ABF ; to PO-AT-SET as line can be ignored. + + LD A,$16 ; 22 decimal + SUB B ; subtract line number to reverse + ; 0 - 22 becomes 22 - 0. + +;; PO-AT-ERR +L0AAC: JP C,L1E9F ; to REPORT-B if higher than 22 decimal + ; Integer out of range. + + INC A ; adjust for system range $01-$17 + LD B,A ; place in line register + INC B ; adjust to system range $02-$18 + BIT 0,(IY+$02) ; TV_FLAG - Lower screen in use ? + JP NZ,L0C55 ; exit to PO-SCR to test for scrolling + + CP (IY+$31) ; Compare against DF_SZ + JP C,L0C86 ; to REPORT-5 if too low + ; Out of screen. + +;; PO-AT-SET +L0ABF: JP L0DD9 ; print position is valid so exit via CL-SET + +; Continue here when dealing with TAB. +; Note. In BASIC, TAB is followed by a 16-bit number and was initially +; designed to work with any output device. + +;; PO-TAB +L0AC2: LD A,H ; transfer parameter to A + ; Losing current character - + ; High byte of TAB parameter. + + +;; PO-FILL +L0AC3: CALL L0B03 ; routine PO-FETCH, HL-addr, BC=line/column. + ; column 1 (right), $21 (left) + ADD A,C ; add operand to current column + DEC A ; range 0 - 31+ + AND $1F ; make range 0 - 31d + RET Z ; return if result zero + + LD D,A ; Counter to D + SET 0,(IY+$01) ; update FLAGS - signal suppress leading space. + +;; PO-SPACE +L0AD0: LD A,$20 ; space character. + CALL L0C3B ; routine PO-SAVE prints the character + ; using alternate set (normal output routine) + DEC D ; decrement counter. + JR NZ,L0AD0 ; to PO-SPACE until done + + RET ; return + +; ---------------------- +; Printable character(s) +; ---------------------- +; This routine prints printable characters and continues into +; the position store routine + +;; PO-ABLE +L0AD9: CALL L0B24 ; routine PO-ANY + ; and continue into position store routine. + +; ------------------------------------- +; Store line, column, and pixel address +; ------------------------------------- +; This routine updates the system variables associated with +; The main screen, lower screen/input buffer or ZX printer. + +;; PO-STORE +L0ADC: BIT 1,(IY+$01) ; test FLAGS - Is printer in use ? + JR NZ,L0AFC ; to PO-ST-PR if so + + BIT 0,(IY+$02) ; TV_FLAG - Lower screen in use ? + JR NZ,L0AF0 ; to PO-ST-E if so + + LD ($5C88),BC ; S_POSN line/column upper screen + LD ($5C84),HL ; DF_CC display file address + RET ; + +; --- + +;; PO-ST-E +L0AF0: LD ($5C8A),BC ; SPOSNL line/column lower screen + LD ($5C82),BC ; ECHO_E line/column input buffer + LD ($5C86),HL ; DFCCL lower screen memory address + RET ; + +; --- + +;; PO-ST-PR +L0AFC: LD (IY+$45),C ; P_POSN column position printer + LD ($5C80),HL ; PR_CC full printer buffer memory address + RET ; + +; ------------------------- +; Fetch position parameters +; ------------------------- +; This routine fetches the line/column and display file address +; of the upper and lower screen or, if the printer is in use, +; the column position and absolute memory address. +; Note. that PR-CC-hi (23681) is used by this routine and the one above +; and if, in accordance with the manual (that says this is unused), the +; location has been used for other purposes, then subsequent output +; to the printer buffer could corrupt a 256-byte section of memory. + +;; PO-FETCH +L0B03: BIT 1,(IY+$01) ; test FLAGS - Is printer in use + JR NZ,L0B1D ; to PO-F-PR if so + + ; assume upper screen + LD BC,($5C88) ; S_POSN + LD HL,($5C84) ; DF_CC display file address + BIT 0,(IY+$02) ; TV_FLAG - Lower screen in use ? + RET Z ; return if upper screen + + ; ah well, was lower screen + LD BC,($5C8A) ; SPOSNL + LD HL,($5C86) ; DFCCL + RET ; return + +; --- + +;; PO-F-PR +L0B1D: LD C,(IY+$45) ; P_POSN column only + LD HL,($5C80) ; PR_CC printer buffer address + RET ; return + +; ------------------- +; Print any character +; ------------------- +; This routine is used to print any character in range 32d - 255d +; It is only called from PO-ABLE which continues into PO-STORE + +;; PO-ANY +L0B24: CP $80 ; ASCII ? + JR C,L0B65 ; to PO-CHAR is so. + + CP $90 ; test if a block graphic character. + JR NC,L0B52 ; to PO-T&UDG to print tokens and udg's + +; The 16 2*2 mosaic characters 128-143 decimal are formed from +; bits 0-3 of the character. + + LD B,A ; save character + CALL L0B38 ; routine PO-GR-1 to construct top half + ; then bottom half. + CALL L0B03 ; routine PO-FETCH fetches print position. + LD DE,$5C92 ; MEM-0 is location of 8 bytes of character + JR L0B7F ; to PR-ALL to print to screen or printer + +; --- + +;; PO-GR-1 +L0B38: LD HL,$5C92 ; address MEM-0 - a temporary buffer in + ; systems variables which is normally used + ; by the calculator. + CALL L0B3E ; routine PO-GR-2 to construct top half + ; and continue into routine to construct + ; bottom half. + +;; PO-GR-2 +L0B3E: RR B ; rotate bit 0/2 to carry + SBC A,A ; result $00 or $FF + AND $0F ; mask off right hand side + LD C,A ; store part in C + RR B ; rotate bit 1/3 of original chr to carry + SBC A,A ; result $00 or $FF + AND $F0 ; mask off left hand side + OR C ; combine with stored pattern + LD C,$04 ; four bytes for top/bottom half + +;; PO-GR-3 +L0B4C: LD (HL),A ; store bit patterns in temporary buffer + INC HL ; next address + DEC C ; jump back to + JR NZ,L0B4C ; to PO-GR-3 until byte is stored 4 times + + RET ; return + +; --- + +; Tokens and User defined graphics are now separated. + +;; PO-T&UDG +L0B52: JP L3B9F ;Spectrum 128 patch + NOP + +L0B56: ADD A,$15 ; add 21d to restore to 0 - 20 + PUSH BC ; save current print position + LD BC,($5C7B) ; fetch UDG to address bit patterns + JR L0B6A ; to PO-CHAR-2 - common code to lay down + ; a bit patterned character + +; --- + +;; PO-T +L0B5F: CALL L0C10 ; routine PO-TOKENS prints tokens + JP L0B03 ; exit via a JUMP to PO-FETCH as this routine + ; must continue into PO-STORE. + ; A JR instruction could be used. + +; This point is used to print ASCII characters 32d - 127d. + +;; PO-CHAR +L0B65: PUSH BC ; save print position + LD BC,($5C36) ; address CHARS + +; This common code is used to transfer the character bytes to memory. + +;; PO-CHAR-2 +L0B6A: EX DE,HL ; transfer destination address to DE + LD HL,$5C3B ; point to FLAGS + RES 0,(HL) ; allow for leading space + CP $20 ; is it a space ? + JR NZ,L0B76 ; to PO-CHAR-3 if not + + SET 0,(HL) ; signal no leading space to FLAGS + +;; PO-CHAR-3 +L0B76: LD H,$00 ; set high byte to 0 + LD L,A ; character to A + ; 0-21 UDG or 32-127 ASCII. + ADD HL,HL ; multiply + ADD HL,HL ; by + ADD HL,HL ; eight + ADD HL,BC ; HL now points to first byte of character + POP BC ; the source address CHARS or UDG + EX DE,HL ; character address to DE + +; -------------------- +; Print all characters +; -------------------- +; This entry point entered from above to print ASCII and UDGs +; but also from earlier to print mosaic characters. +; HL=destination +; DE=character source +; BC=line/column + +;; PR-ALL +L0B7F: LD A,C ; column to A + DEC A ; move right + LD A,$21 ; pre-load with leftmost position + JR NZ,L0B93 ; but if not zero to PR-ALL-1 + + DEC B ; down one line + LD C,A ; load C with $21 + BIT 1,(IY+$01) ; test FLAGS - Is printer in use + JR Z,L0B93 ; to PR-ALL-1 if not + + PUSH DE ; save source address + CALL L0ECD ; routine COPY-BUFF outputs line to printer + POP DE ; restore character source address + LD A,C ; the new column number ($21) to C + +;; PR-ALL-1 +L0B93: CP C ; this test is really for screen - new line ? + PUSH DE ; save source + + CALL Z,L0C55 ; routine PO-SCR considers scrolling + + POP DE ; restore source + PUSH BC ; save line/column + PUSH HL ; and destination + LD A,($5C91) ; fetch P_FLAG to accumulator + LD B,$FF ; prepare OVER mask in B. + RRA ; bit 0 set if OVER 1 + JR C,L0BA4 ; to PR-ALL-2 + + INC B ; set OVER mask to 0 + +;; PR-ALL-2 +L0BA4: RRA ; skip bit 1 of P_FLAG + RRA ; bit 2 is INVERSE + SBC A,A ; will be FF for INVERSE 1 else zero + LD C,A ; transfer INVERSE mask to C + LD A,$08 ; prepare to count 8 bytes + AND A ; clear carry to signal screen + BIT 1,(IY+$01) ; test FLAGS - is printer in use ? + JR Z,L0BB6 ; to PR-ALL-3 if screen + + SET 1,(IY+$30) ; update FLAGS2 - signal printer buffer has + ; been used. + SCF ; set carry flag to signal printer. + +;; PR-ALL-3 +L0BB6: EX DE,HL ; now HL=source, DE=destination + +;; PR-ALL-4 +L0BB7: EX AF,AF' ; save printer/screen flag + LD A,(DE) ; fetch existing destination byte + AND B ; consider OVER + XOR (HL) ; now XOR with source + XOR C ; now with INVERSE MASK + LD (DE),A ; update screen/printer + EX AF,AF' ; restore flag + JR C,L0BD3 ; to PR-ALL-6 - printer address update + + INC D ; gives next pixel line down screen + +;; PR-ALL-5 +L0BC1: INC HL ; address next character byte + DEC A ; the byte count is decremented + JR NZ,L0BB7 ; back to PR-ALL-4 for all 8 bytes + + EX DE,HL ; destination to HL + DEC H ; bring back to last updated screen position + BIT 1,(IY+$01) ; test FLAGS - is printer in use ? + CALL Z,L0BDB ; if not, call routine PO-ATTR to update + ; corresponding colour attribute. + POP HL ; restore original screen/printer position + POP BC ; and line column + DEC C ; move column to right + INC HL ; increase screen/printer position + RET ; return and continue into PO-STORE + ; within PO-ABLE + +; --- + +; This branch is used to update the printer position by 32 places +; Note. The high byte of the address D remains constant (which it should). + +;; PR-ALL-6 +L0BD3: EX AF,AF' ; save the flag + LD A,$20 ; load A with 32 decimal + ADD A,E ; add this to E + LD E,A ; and store result in E + EX AF,AF' ; fetch the flag + JR L0BC1 ; back to PR-ALL-5 + +; ------------- +; Set attribute +; ------------- +; This routine is entered with the HL register holding the last screen +; address to be updated by PRINT or PLOT. +; The Spectrum screen arrangement leads to the L register holding +; the correct value for the attribute file and it is only necessary +; to manipulate H to form the correct colour attribute address. + +;; PO-ATTR +L0BDB: LD A,H ; fetch high byte $40 - $57 + RRCA ; shift + RRCA ; bits 3 and 4 + RRCA ; to right. + AND $03 ; range is now 0 - 2 + OR $58 ; form correct high byte for third of screen + LD H,A ; HL is now correct + LD DE,($5C8F) ; make D hold ATTR_T, E hold MASK-T + LD A,(HL) ; fetch existing attribute + XOR E ; apply masks + AND D ; + XOR E ; + BIT 6,(IY+$57) ; test P_FLAG - is this PAPER 9 ?? + JR Z,L0BFA ; skip to PO-ATTR-1 if not. + + AND $C7 ; set paper + BIT 2,A ; to contrast with ink + JR NZ,L0BFA ; skip to PO-ATTR-1 + + XOR $38 ; + +;; PO-ATTR-1 +L0BFA: BIT 4,(IY+$57) ; test P_FLAG - Is this INK 9 ?? + JR Z,L0C08 ; skip to PO-ATTR-2 if not + + AND $F8 ; make ink + BIT 5,A ; contrast with paper. + JR NZ,L0C08 ; to PO-ATTR-2 + + XOR $07 ; + +;; PO-ATTR-2 +L0C08: LD (HL),A ; save the new attribute. + RET ; return. + +; ---------------- +; Message printing +; ---------------- +; This entry point is used to print tape, boot-up, scroll? and error messages +; On entry the DE register points to an initial step-over byte or +; the inverted end-marker of the previous entry in the table. +; A contains the message number, often zero to print first message. +; (HL has nothing important usually P_FLAG) + +;; PO-MSG +L0C0A: PUSH HL ; put hi-byte zero on stack to suppress + LD H,$00 ; trailing spaces + EX (SP),HL ; ld h,0; push hl would have done ?. + JR L0C14 ; forward to PO-TABLE. + +; --- + +; This entry point prints the BASIC keywords, '<>' etc. from alt set + +;; PO-TOKENS +L0C10: LD DE,L0095 ; address: TKN-TABLE + PUSH AF ; save the token number to control + ; trailing spaces - see later * + +;; PO-TABLE +L0C14: CALL L0C41 ; routine PO-SEARCH will set carry for + ; all messages and function words. +L0C17: JR C,L0C22 ; forward to PO-EACH if not a command, + ; '<>' etc. + + LD A,$20 ; prepare leading space + BIT 0,(IY+$01) ; test FLAGS - leading space if not set + CALL Z,L0C3B ; routine PO-SAVE to print a space + ; without disturbing registers + +;; PO-EACH +L0C22: LD A,(DE) ; fetch character + AND $7F ; remove any inverted bit + CALL L0C3B ; routine PO-SAVE to print using alternate + ; set of registers. + LD A,(DE) ; re-fetch character. + INC DE ; address next + ADD A,A ; was character inverted ? + ; (this also doubles character) + JR NC,L0C22 ; back to PO-EACH if not + + POP DE ; * re-fetch trailing space flag to D (was A) + CP $48 ; was last character '$' ($24*2) + JR Z,L0C35 ; forward to PO-TR-SP to consider trailing + ; space if so. + + CP $82 ; was it < 'A' i.e. '#','>','=' from tokens + ; or ' ','.' (from tape) or '?' from scroll + RET C ; no trailing space + +;; PO-TR-SP +L0C35: LD A,D ; the trailing space flag (zero if an error msg) + CP $03 ; test against RND, INKEY$ and PI + ; which have no parameters and + RET C ; therefore no trailing space so return. + + LD A,$20 ; else continue and print a trailing space. + +; ------------------------- +; Handle recursive printing +; ------------------------- +; This routine which is part of PRINT-OUT allows RST $10 to be +; used recursively to print tokens and the spaces associated with them. + +;; PO-SAVE +L0C3B: PUSH DE ; save DE as CALL-SUB doesn't. + EXX ; switch in main set + + RST 10H ; PRINT-A prints using this alternate set. + + EXX ; back to this alternate set. + POP DE ; restore initial DE. + RET ; return. + +; ------------ +; Table search +; ------------ +; This subroutine searches a message or the token table for the +; message number held in A. DE holds the address of the table. + +;; PO-SEARCH +L0C41: PUSH AF ; save the message/token number + EX DE,HL ; transfer DE to HL + INC A ; adjust for initial step-over byte + +;; PO-STEP +L0C44: BIT 7,(HL) ; is character inverted ? + INC HL ; address next + JR Z,L0C44 ; back to PO-STEP if not inverted. + + DEC A ; decrease counter + JR NZ,L0C44 ; back to PO-STEP if not zero + + EX DE,HL ; transfer address to DE + POP AF ; restore message/token number + CP $20 ; return with carry set + RET C ; for all messages and function tokens + + LD A,(DE) ; test first character of token + SUB $41 ; and return with carry set + RET ; if it is less that 'A' + ; i.e. '<>', '<=', '>=' + +; --------------- +; Test for scroll +; --------------- +; This test routine is called when printing carriage return, when considering +; PRINT AT and from the general PRINT ALL characters routine to test if +; scrolling is required, prompting the user if necessary. +; This is therefore using the alternate set. +; The B register holds the current line. + +;; PO-SCR +L0C55: BIT 1,(IY+$01) ; test FLAGS - is printer in use ? + RET NZ ; return immediately if so. + + LD DE,L0DD9 ; set DE to address: CL-SET + PUSH DE ; and push for return address. + LD A,B ; transfer the line to A. + BIT 0,(IY+$02) ; test TV_FLAG - Lower screen in use ? + JP NZ,L0D02 ; jump forward to PO-SCR-4 if so. + + CP (IY+$31) ; greater than DF_SZ display file size ? + JR C,L0C86 ; forward to REPORT-5 if less. + ; 'Out of screen' + + RET NZ ; return (via CL-SET) if greater + + BIT 4,(IY+$02) ; test TV_FLAG - Automatic listing ? + JR Z,L0C88 ; forward to PO-SCR-2 if not. + + LD E,(IY+$2D) ; fetch BREG - the count of scroll lines to E. + DEC E ; decrease and jump + JR Z,L0CD2 ; to PO-SCR-3 if zero and scrolling required. + + LD A,$00 ; explicit - select channel zero. + CALL L1601 ; routine CHAN-OPEN opens it. + + LD SP,($5C3F) ; set stack pointer to LIST_SP + + RES 4,(IY+$02) ; reset TV_FLAG - signal auto listing finished. + RET ; return ignoring pushed value, CL-SET + ; to MAIN or EDITOR without updating + ; print position -> + +; --- + + +;; REPORT-5 +L0C86: RST 08H ; ERROR-1 + DEFB $04 ; Error Report: Out of screen + +; continue here if not an automatic listing. + +;; PO-SCR-2 +L0C88: DEC (IY+$52) ; decrease SCR_CT + JR NZ,L0CD2 ; forward to PO-SCR-3 to scroll display if + ; result not zero. + +; now produce prompt. + + LD A,$18 ; reset + SUB B ; the + LD ($5C8C),A ; SCR_CT scroll count + LD HL,($5C8F) ; L=ATTR_T, H=MASK_T + PUSH HL ; save on stack + LD A,($5C91) ; P_FLAG + PUSH AF ; save on stack to prevent lower screen + ; attributes (BORDCR etc.) being applied. + LD A,$FD ; select system channel 'K' + CALL L1601 ; routine CHAN-OPEN opens it + XOR A ; clear to address message directly + LD DE,L0CF8 ; make DE address: scrl-mssg + CALL L0C0A ; routine PO-MSG prints to lower screen + SET 5,(IY+$02) ; set TV_FLAG - signal lower screen requires + ; clearing + LD HL,$5C3B ; make HL address FLAGS + SET 3,(HL) ; signal 'L' mode. + RES 5,(HL) ; signal 'no new key'. + EXX ; switch to main set. + ; as calling chr input from alternative set. + CALL L15D4 ; routine WAIT-KEY waits for new key + ; Note. this is the right routine but the + ; stream in use is unsatisfactory. From the + ; choices available, it is however the best. + + EXX ; switch back to alternate set. + CP $20 ; space is considered as BREAK + JR Z,L0D00 ; forward to REPORT-D if so + ; 'BREAK - CONT repeats' + + CP $E2 ; is character 'STOP' ? + JR Z,L0D00 ; forward to REPORT-D if so + + OR $20 ; convert to lower-case + CP $6E ; is character 'n' ? + JR Z,L0D00 ; forward to REPORT-D if so else scroll. + + LD A,$FE ; select system channel 'S' + CALL L1601 ; routine CHAN-OPEN + POP AF ; restore original P_FLAG + LD ($5C91),A ; and save in P_FLAG. + POP HL ; restore original ATTR_T, MASK_T + LD ($5C8F),HL ; and reset ATTR_T, MASK-T as 'scroll?' has + ; been printed. + +;; PO-SCR-3 +L0CD2: CALL L0DFE ; routine CL-SC-ALL to scroll whole display + LD B,(IY+$31) ; fetch DF_SZ to B + INC B ; increase to address last line of display + LD C,$21 ; set C to $21 (was $21 from above routine) + PUSH BC ; save the line and column in BC. + + CALL L0E9B ; routine CL-ADDR finds display address. + + LD A,H ; now find the corresponding attribute byte + RRCA ; (this code sequence is used twice + RRCA ; elsewhere and is a candidate for + RRCA ; a subroutine.) + AND $03 ; + OR $58 ; + LD H,A ; + + LD DE,$5AE0 ; start of last 'line' of attribute area + LD A,(DE) ; get attribute for last line + LD C,(HL) ; transfer to base line of upper part + LD B,$20 ; there are thirty two bytes + EX DE,HL ; swap the pointers. + +;; PO-SCR-3A +L0CF0: LD (DE),A ; transfer + LD (HL),C ; attributes. + INC DE ; address next. + INC HL ; address next. + DJNZ L0CF0 ; loop back to PO-SCR-3A for all adjacent + ; attribute lines. + + POP BC ; restore the line/column. + RET ; return via CL-SET (was pushed on stack). + +; --- + +; The message 'scroll?' appears here with last byte inverted. + +;; scrl-mssg +L0CF8: DEFB $80 ; initial step-over byte. + DEFM "scroll" + DEFB '?'+$80 + +;; REPORT-D +L0D00: RST 08H ; ERROR-1 + DEFB $0C ; Error Report: BREAK - CONT repeats + +; continue here if using lower display - A holds line number. + +;; PO-SCR-4 +L0D02: CP $02 ; is line number less than 2 ? + JR C,L0C86 ; to REPORT-5 if so + ; 'Out of Screen'. + + ADD A,(IY+$31) ; add DF_SZ + SUB $19 ; + RET NC ; return if scrolling unnecessary + + NEG ; Negate to give number of scrolls required. + PUSH BC ; save line/column + LD B,A ; count to B + LD HL,($5C8F) ; fetch current ATTR_T, MASK_T to HL. + PUSH HL ; and save + LD HL,($5C91) ; fetch P_FLAG + PUSH HL ; and save. + ; to prevent corruption by input AT + + CALL L0D4D ; routine TEMPS sets to BORDCR etc + LD A,B ; transfer scroll number to A. + +;; PO-SCR-4A +L0D1C: PUSH AF ; save scroll number. + LD HL,$5C6B ; address DF_SZ + LD B,(HL) ; fetch old value + LD A,B ; transfer to A + INC A ; and increment + LD (HL),A ; then put back. + LD HL,$5C89 ; address S_POSN_hi - line + CP (HL) ; compare + JR C,L0D2D ; forward to PO-SCR-4B if scrolling required + + INC (HL) ; else increment S_POSN_hi + LD B,$18 ; set count to whole display ?? + ; Note. should be $17 and the top line + ; will be scrolled into the ROM which + ; is harmless on the standard set up. + +;; PO-SCR-4B +L0D2D: CALL L0E00 ; routine CL-SCROLL scrolls B lines + POP AF ; restore scroll counter. + DEC A ; decrease + JR NZ,L0D1C ; back to to PO-SCR-4A until done + + POP HL ; restore original P_FLAG. + LD (IY+$57),L ; and overwrite system variable P_FLAG. + + POP HL ; restore original ATTR_T/MASK_T. + LD ($5C8F),HL ; and update system variables. + + LD BC,($5C88) ; fetch S_POSN to BC. + RES 0,(IY+$02) ; signal to TV_FLAG - main screen in use. + CALL L0DD9 ; call routine CL-SET for upper display. + + SET 0,(IY+$02) ; signal to TV_FLAG - lower screen in use. + POP BC ; restore line/column + RET ; return via CL-SET for lower display. + +; ---------------------- +; Temporary colour items +; ---------------------- +; This subroutine is called 11 times to copy the permanent colour items +; to the temporary ones. + +;; TEMPS +L0D4D: XOR A ; clear the accumulator + LD HL,($5C8D) ; fetch L=ATTR_P and H=MASK_P + BIT 0,(IY+$02) ; test TV_FLAG - is lower screen in use ? + JR Z,L0D5B ; skip to TEMPS-1 if not + + LD H,A ; set H, MASK P, to 00000000. + LD L,(IY+$0E) ; fetch BORDCR to L which is used for lower + ; screen. + +;; TEMPS-1 +L0D5B: LD ($5C8F),HL ; transfer values to ATTR_T and MASK_T + +; for the print flag the permanent values are odd bits, temporary even bits. + + LD HL,$5C91 ; address P_FLAG. + JR NZ,L0D65 ; skip to TEMPS-2 if lower screen using A=0. + + LD A,(HL) ; else pick up flag bits. + RRCA ; rotate permanent bits to temporary bits. + +;; TEMPS-2 +L0D65: XOR (HL) ; + AND $55 ; BIN 01010101 + XOR (HL) ; permanent now as original + LD (HL),A ; apply permanent bits to temporary bits. + RET ; and return. + +; ------------------ +; Handle CLS command +; ------------------ +; clears the display. +; if it's difficult to write it should be difficult to read. + +;; CLS +L0D6B: CALL L0DAF ; routine CL-ALL clears display and + ; resets attributes to permanent. + ; re-attaches it to this computer. + +; this routine called from INPUT, ** + +;; CLS-LOWER +L0D6E: LD HL,$5C3C ; address System Variable TV_FLAG. + RES 5,(HL) ; TV_FLAG - signal do not clear lower screen. + SET 0,(HL) ; TV_FLAG - signal lower screen in use. + CALL L0D4D ; routine TEMPS picks up temporary colours. + LD B,(IY+$31) ; fetch lower screen DF_SZ + CALL L0E44 ; routine CL-LINE clears lower part + ; and sets permanent attributes. + + LD HL,$5AC0 ; fetch attribute address leftmost cell, + ; second line up. + LD A,($5C8D) ; fetch permanent attribute from ATTR_P. + DEC B ; decrement lower screen display file size + JR L0D8E ; forward to CLS-3 -> + +; --- + +;; CLS-1 +L0D87: LD C,$20 ; set counter to 32 characters per line + +;; CLS-2 +L0D89: DEC HL ; decrease attribute address. + LD (HL),A ; and place attributes in next line up. + DEC C ; decrease 32 counter. + JR NZ,L0D89 ; loop back to CLS-2 until all 32 done. + +;; CLS-3 +L0D8E: DJNZ L0D87 ; decrease B counter and back to CLS-1 + ; if not zero. + + LD (IY+$31),$02 ; set DF_SZ lower screen to 2 + +; This entry point is called from CL-ALL below to +; reset the system channel input and output addresses to normal. + +;; CL-CHAN +L0D94: LD A,$FD ; select system channel 'K' + CALL L1601 ; routine CHAN-OPEN opens it. + LD HL,($5C51) ; fetch CURCHL to HL to address current channel + LD DE,L09F4 ; set address to PRINT-OUT for first pass. + AND A ; clear carry for first pass. + +;; CL-CHAN-A +L0DA0: LD (HL),E ; insert output address first pass. + INC HL ; or input address on second pass. + LD (HL),D ; + INC HL ; + LD DE,L10A8 ; fetch address KEY-INPUT for second pass + CCF ; complement carry flag - will set on pass 1. + + JR C,L0DA0 ; back to CL-CHAN-A if first pass else done. + + LD BC,$1721 ; line 23 for lower screen + JR L0DD9 ; exit via CL-SET to set column + ; for lower display + +; --------------------------- +; Clearing whole display area +; --------------------------- +; This subroutine called from CLS, AUTO-LIST and MAIN-3 +; clears 24 lines of the display and resets the relevant system variables +; and system channels. + +;; CL-ALL +L0DAF: LD HL,$0000 ; initialize plot coordinates. + LD ($5C7D),HL ; set COORDS to 0,0. + RES 0,(IY+$30) ; update FLAGS2 - signal main screen is clear. + + CALL L0D94 ; routine CL-CHAN makes channel 'K' 'normal'. + + LD A,$FE ; select system channel 'S' + CALL L1601 ; routine CHAN-OPEN opens it + CALL L0D4D ; routine TEMPS picks up permanent values. + LD B,$18 ; There are 24 lines. + CALL L0E44 ; routine CL-LINE clears 24 text lines + ; (and sets BC to $1821) + + LD HL,($5C51) ; fetch CURCHL make HL address current + ; channel 'S' + LD DE,L09F4 ; address: PRINT-OUT + LD (HL),E ; is made + INC HL ; the normal + LD (HL),D ; output address. + + LD (IY+$52),$01 ; set SCR_CT - scroll count is set to default. + ; Note. BC already contains $1821. + LD BC,$1821 ; reset column and line to 0,0 + ; and continue into CL-SET, below, exiting + ; via PO-STORE (for upper screen). + +; --------------------------- +; Set line and column numbers +; --------------------------- +; This important subroutine is used to calculate the character output +; address for screens or printer based on the line/column for screens +; or the column for printer. + +;; CL-SET +L0DD9: LD HL,$5B00 ; the base address of printer buffer + BIT 1,(IY+$01) ; test FLAGS - is printer in use ? + JR NZ,L0DF4 ; forward to CL-SET-2 if so. + + LD A,B ; transfer line to A. + BIT 0,(IY+$02) ; test TV_FLAG - lower screen in use ? + JR Z,L0DEE ; skip to CL-SET-1 if handling upper part + + ADD A,(IY+$31) ; add DF_SZ for lower screen + SUB $18 ; and adjust. + +;; CL-SET-1 +L0DEE: PUSH BC ; save the line/column. + LD B,A ; transfer line to B + ; (adjusted if lower screen) + + CALL L0E9B ; routine CL-ADDR calculates address at left + ; of screen. + POP BC ; restore the line/column. + +;; CL-SET-2 +L0DF4: LD A,$21 ; the column $1-$21 is reversed + SUB C ; to range $00 - $20 + LD E,A ; now transfer to DE + LD D,$00 ; prepare for addition + ADD HL,DE ; and add to base address + JP L0ADC ; exit via PO-STORE to update relevant + ; system variables. +; ---------------- +; Handle scrolling +; ---------------- +; The routine CL-SC-ALL is called once from PO to scroll all the display +; and from the routine CL-SCROLL, once, to scroll part of the display. + +;; CL-SC-ALL +L0DFE: LD B,$17 ; scroll 23 lines, after 'scroll?'. + +;; CL-SCROLL +L0E00: CALL L0E9B ; routine CL-ADDR gets screen address in HL. + LD C,$08 ; there are 8 pixel lines to scroll. + +;; CL-SCR-1 +L0E05: PUSH BC ; save counters. + PUSH HL ; and initial address. + LD A,B ; get line count. + AND $07 ; will set zero if all third to be scrolled. + LD A,B ; re-fetch the line count. + JR NZ,L0E19 ; forward to CL-SCR-3 if partial scroll. + +; HL points to top line of third and must be copied to bottom of previous 3rd. +; ( so HL = $4800 or $5000 ) ( but also sometimes $4000 ) + +;; CL-SCR-2 +L0E0D: EX DE,HL ; copy HL to DE. + LD HL,$F8E0 ; subtract $08 from H and add $E0 to L - + ADD HL,DE ; to make destination bottom line of previous + ; third. + EX DE,HL ; restore the source and destination. + LD BC,$0020 ; thirty-two bytes are to be copied. + DEC A ; decrement the line count. + LDIR ; copy a pixel line to previous third. + +;; CL-SCR-3 +L0E19: EX DE,HL ; save source in DE. + LD HL,$FFE0 ; load the value -32. + ADD HL,DE ; add to form destination in HL. + EX DE,HL ; switch source and destination + LD B,A ; save the count in B. + AND $07 ; mask to find count applicable to current + RRCA ; third and + RRCA ; multiply by + RRCA ; thirty two (same as 5 RLCAs) + + LD C,A ; transfer byte count to C ($E0 at most) + LD A,B ; store line count to A + LD B,$00 ; make B zero + LDIR ; copy bytes (BC=0, H incremented, L=0) + LD B,$07 ; set B to 7, C is zero. + ADD HL,BC ; add 7 to H to address next third. + AND $F8 ; has last third been done ? + JR NZ,L0E0D ; back to CL-SCR-2 if not + + POP HL ; restore topmost address. + INC H ; next pixel line down. + POP BC ; restore counts. + DEC C ; reduce pixel line count. + JR NZ,L0E05 ; back to CL-SCR-1 if all eight not done. + + CALL L0E88 ; routine CL-ATTR gets address in attributes + ; from current 'ninth line', count in BC. + LD HL,$FFE0 ; set HL to the 16-bit value -32. + ADD HL,DE ; and add to form destination address. + EX DE,HL ; swap source and destination addresses. + LDIR ; copy bytes scrolling the linear attributes. + LD B,$01 ; continue to clear the bottom line. + +; --------------------------- +; Clear text lines of display +; --------------------------- +; This subroutine, called from CL-ALL, CLS-LOWER and AUTO-LIST and above, +; clears text lines at bottom of display. +; The B register holds on entry the number of lines to be cleared 1-24. + +;; CL-LINE +L0E44: PUSH BC ; save line count + CALL L0E9B ; routine CL-ADDR gets top address + LD C,$08 ; there are eight screen lines to a text line. + +;; CL-LINE-1 +L0E4A: PUSH BC ; save pixel line count + PUSH HL ; and save the address + LD A,B ; transfer the line to A (1-24). + +;; CL-LINE-2 +L0E4D: AND $07 ; mask 0-7 to consider thirds at a time + RRCA ; multiply + RRCA ; by 32 (same as five RLCA instructions) + RRCA ; now 32 - 256(0) + LD C,A ; store result in C + LD A,B ; save line in A (1-24) + LD B,$00 ; set high byte to 0, prepare for ldir. + DEC C ; decrement count 31-255. + LD D,H ; copy HL + LD E,L ; to DE. + LD (HL),$00 ; blank the first byte. + INC DE ; make DE point to next byte. + LDIR ; ldir will clear lines. + LD DE,$0701 ; now address next third adjusting + ADD HL,DE ; register E to address left hand side + DEC A ; decrease the line count. + AND $F8 ; will be 16, 8 or 0 (AND $18 will do). + LD B,A ; transfer count to B. + JR NZ,L0E4D ; back to CL-LINE-2 if 16 or 8 to do + ; the next third. + + POP HL ; restore start address. + INC H ; address next line down. + POP BC ; fetch counts. + DEC C ; decrement pixel line count + JR NZ,L0E4A ; back to CL-LINE-1 till all done. + + CALL L0E88 ; routine CL-ATTR gets attribute address + ; in DE and B * 32 in BC. + LD H,D ; transfer the address + LD L,E ; to HL. + + INC DE ; make DE point to next location. + + LD A,($5C8D) ; fetch ATTR_P - permanent attributes + BIT 0,(IY+$02) ; test TV_FLAG - lower screen in use ? + JR Z,L0E80 ; skip to CL-LINE-3 if not. + + LD A,($5C48) ; else lower screen uses BORDCR as attribute. + +;; CL-LINE-3 +L0E80: LD (HL),A ; put attribute in first byte. + DEC BC ; decrement the counter. + LDIR ; copy bytes to set all attributes. + POP BC ; restore the line $01-$24. + LD C,$21 ; make column $21. (No use is made of this) + RET ; return to the calling routine. + +; ------------------ +; Attribute handling +; ------------------ +; This subroutine is called from CL-LINE or CL-SCROLL with the HL register +; pointing to the 'ninth' line and H needs to be decremented before or after +; the division. Had it been done first then either present code or that used +; at the start of PO-ATTR could have been used. +; The Spectrum screen arrangement leads to the L register holding already +; the correct value for the attribute file and it is only necessary +; to manipulate H to form the correct colour attribute address. + +;; CL-ATTR +L0E88: LD A,H ; fetch H to A - $48, $50, or $58. + RRCA ; divide by + RRCA ; eight. + RRCA ; $09, $0A or $0B. + DEC A ; $08, $09 or $0A. + OR $50 ; $58, $59 or $5A. + LD H,A ; save high byte of attributes. + + EX DE,HL ; transfer attribute address to DE + LD H,C ; set H to zero - from last LDIR. + LD L,B ; load L with the line from B. + ADD HL,HL ; multiply + ADD HL,HL ; by + ADD HL,HL ; thirty two + ADD HL,HL ; to give count of attribute + ADD HL,HL ; cells to end of display. + + LD B,H ; transfer result + LD C,L ; to register BC. + + RET ; and return. + +; ------------------------------- +; Handle display with line number +; ------------------------------- +; This subroutine is called from four places to calculate the address +; of the start of a screen character line which is supplied in B. + +;; CL-ADDR +L0E9B: LD A,$18 ; reverse the line number + SUB B ; to range $00 - $17. + LD D,A ; save line in D for later. + RRCA ; multiply + RRCA ; by + RRCA ; thirty-two. + + AND $E0 ; mask off low bits to make + LD L,A ; L a multiple of 32. + + LD A,D ; bring back the line to A. + + AND $18 ; now $00, $08 or $10. + + OR $40 ; add the base address of screen. + + LD H,A ; HL now has the correct address. + RET ; return. + +; ------------------- +; Handle COPY command +; ------------------- +; This command copies the top 176 lines to the ZX Printer +; It is popular to call this from machine code at point +; L0EAF with B holding 192 (and interrupts disabled) for a full-screen +; copy. This particularly applies to 16K Spectrums as time-critical +; machine code routines cannot be written in the first 16K of RAM as +; it is shared with the ULA which has precedence over the Z80 chip. + +;; COPY + +L0EAC: DI ; disable interrupts as this is time-critical. + + LD B,$B0 ; top 176 lines. +L0EAF: LD HL,$4000 ; address start of the display file. + +; now enter a loop to handle each pixel line. + +;; COPY-1 +L0EB2: PUSH HL ; save the screen address. + PUSH BC ; and the line counter. + + CALL L0EF4 ; routine COPY-LINE outputs one line. + + POP BC ; restore the line counter. + POP HL ; and display address. + INC H ; next line down screen within 'thirds'. + LD A,H ; high byte to A. + AND $07 ; result will be zero if we have left third. + JR NZ,L0EC9 ; forward to COPY-2 if not to continue loop. + + LD A,L ; consider low byte first. + ADD A,$20 ; increase by 32 - sets carry if back to zero. + LD L,A ; will be next group of 8. + CCF ; complement - carry set if more lines in + ; the previous third. + SBC A,A ; will be FF, if more, else 00. + AND $F8 ; will be F8 (-8) or 00. + ADD A,H ; that is subtract 8, if more to do in third. + LD H,A ; and reset address. + +;; COPY-2 +L0EC9: DJNZ L0EB2 ; back to COPY-1 for all lines. + + JR L0EDA ; forward to COPY-END to switch off the printer + ; motor and enable interrupts. + ; Note. Nothing else required. + +; ------------------------------ +; Pass printer buffer to printer +; ------------------------------ +; This routine is used to copy 8 text lines from the printer buffer +; to the ZX Printer. These text lines are mapped linearly so HL does +; not need to be adjusted at the end of each line. + +;; COPY-BUFF +L0ECD: DI ; disable interrupts + LD HL,$5B00 ; the base address of the Printer Buffer. + LD B,$08 ; set count to 8 lines of 32 bytes. + +;; COPY-3 +L0ED3: PUSH BC ; save counter. + CALL L0EF4 ; routine COPY-LINE outputs 32 bytes + POP BC ; restore counter. + DJNZ L0ED3 ; loop back to COPY-3 for all 8 lines. + ; then stop motor and clear buffer. + +; Note. the COPY command rejoins here, essentially to execute the next +; three instructions. + +;; COPY-END +L0EDA: LD A,$04 ; output value 4 to port + OUT ($FB),A ; to stop the slowed printer motor. + EI ; enable interrupts. + +; -------------------- +; Clear Printer Buffer +; -------------------- +; This routine clears an arbitrary 256 bytes of memory. +; Note. The routine seems designed to clear a buffer that follows the +; system variables. +; The routine should check a flag or HL address and simply return if COPY +; is in use. +; (T-ADDR-lo would work for the system but not if COPY called externally.) +; As a consequence of this omission the buffer will needlessly +; be cleared when COPY is used and the screen/printer position may be set to +; the start of the buffer and the line number to 0 (B) +; giving an 'Out of Screen' error. +; There seems to have been an unsuccessful attempt to circumvent the use +; of PR_CC_hi. + +;; CLEAR-PRB +L0EDF: LD HL,$5B00 ; the location of the buffer. + LD (IY+$46),L ; update PR_CC_lo - set to zero - superfluous. + XOR A ; clear the accumulator. + LD B,A ; set count to 256 bytes. + +;; PRB-BYTES +L0EE7: LD (HL),A ; set addressed location to zero. + INC HL ; address next byte - Note. not INC L. + DJNZ L0EE7 ; back to PRB-BYTES. repeat for 256 bytes. + + RES 1,(IY+$30) ; set FLAGS2 - signal printer buffer is clear. + LD C,$21 ; set the column position . + JP L0DD9 ; exit via CL-SET and then PO-STORE. + +; ----------------- +; Copy line routine +; ----------------- +; This routine is called from COPY and COPY-BUFF to output a line of +; 32 bytes to the ZX Printer. +; Output to port $FB - +; bit 7 set - activate stylus. +; bit 7 low - deactivate stylus. +; bit 2 set - stops printer. +; bit 2 reset - starts printer +; bit 1 set - slows printer. +; bit 1 reset - normal speed. + +;; COPY-LINE +L0EF4: LD A,B ; fetch the counter 1-8 or 1-176 + CP $03 ; is it 01 or 02 ?. + SBC A,A ; result is $FF if so else $00. + AND $02 ; result is 02 now else 00. + ; bit 1 set slows the printer. + OUT ($FB),A ; slow the printer for the + ; last two lines. + LD D,A ; save the mask to control the printer later. + +;; COPY-L-1 +L0EFD: CALL L1F54 ; call BREAK-KEY to read keyboard immediately. + JR C,L0F0C ; forward to COPY-L-2 if 'break' not pressed. + + LD A,$04 ; else stop the + OUT ($FB),A ; printer motor. + EI ; enable interrupts. + CALL L0EDF ; call routine CLEAR-PRB. + ; Note. should not be cleared if COPY in use. + +;; REPORT-Dc +L0F0A: RST 08H ; ERROR-1 + DEFB $0C ; Error Report: BREAK - CONT repeats + +;; COPY-L-2 +L0F0C: IN A,($FB) ; test now to see if + ADD A,A ; a printer is attached. + RET M ; return if not - but continue with parent + ; command. + + JR NC,L0EFD ; back to COPY-L-1 if stylus of printer not + ; in position. + + LD C,$20 ; set count to 32 bytes. + +;; COPY-L-3 +L0F14: LD E,(HL) ; fetch a byte from line. + INC HL ; address next location. Note. not INC L. + LD B,$08 ; count the bits. + +;; COPY-L-4 +L0F18: RL D ; prepare mask to receive bit. + RL E ; rotate leftmost print bit to carry + RR D ; and back to bit 7 of D restoring bit 1 + +;; COPY-L-5 +L0F1E: IN A,($FB) ; read the port. + RRA ; bit 0 to carry. + JR NC,L0F1E ; back to COPY-L-5 if stylus not in position. + + LD A,D ; transfer command bits to A. + OUT ($FB),A ; and output to port. + DJNZ L0F18 ; loop back to COPY-L-4 for all 8 bits. + + DEC C ; decrease the byte count. + JR NZ,L0F14 ; back to COPY-L-3 until 256 bits done. + + RET ; return to calling routine COPY/COPY-BUFF. + + +; ---------------------------------- +; Editor routine for BASIC and INPUT +; ---------------------------------- +; The editor is called to prepare or edit a BASIC line. +; It is also called from INPUT to input a numeric or string expression. +; The behaviour and options are quite different in the various modes +; and distinguished by bit 5 of FLAGX. +; +; This is a compact and highly versatile routine. + +;; EDITOR +L0F2C: LD HL,($5C3D) ; fetch ERR_SP + PUSH HL ; save on stack + +;; ED-AGAIN +L0F30: LD HL,L107F ; address: ED-ERROR + PUSH HL ; save address on stack and + LD ($5C3D),SP ; make ERR_SP point to it. + +; Note. While in editing/input mode should an error occur then RST 08 will +; update X_PTR to the location reached by CH_ADD and jump to ED-ERROR +; where the error will be cancelled and the loop begin again from ED-AGAIN +; above. The position of the error will be apparent when the lower screen is +; reprinted. If no error then the re-iteration is to ED-LOOP below when +; input is arriving from the keyboard. + +;; ED-LOOP +L0F38: CALL L15D4 ; routine WAIT-KEY gets key possibly + ; changing the mode. + PUSH AF ; save key. + LD D,$00 ; and give a short click based + LD E,(IY-$01) ; on PIP value for duration. + LD HL,$00C8 ; and pitch. + CALL L03B5 ; routine BEEPER gives click - effective + ; with rubber keyboard. + POP AF ; get saved key value. + LD HL,L0F38 ; address: ED-LOOP is loaded to HL. + PUSH HL ; and pushed onto stack. + +; At this point there is a looping return address on the stack, an error +; handler and an input stream set up to supply characters. +; The character that has been received can now be processed. + + CP $18 ; range 24 to 255 ? + JR NC,L0F81 ; forward to ADD-CHAR if so. + + CP $07 ; lower than 7 ? + JR C,L0F81 ; forward to ADD-CHAR also. + ; Note. This is a 'bug' and chr$ 6, the comma + ; control character, should have had an + ; entry in the ED-KEYS table. + ; Steven Vickers, 1984, Pitman. + + CP $10 ; less than 16 ? + JR C,L0F92 ; forward to ED-KEYS if editing control + ; range 7 to 15 dealt with by a table + + LD BC,$0002 ; prepare for ink/paper etc. + LD D,A ; save character in D + CP $16 ; is it ink/paper/bright etc. ? + JR C,L0F6C ; forward to ED-CONTR if so + + ; leaves 22d AT and 23d TAB + ; which can't be entered via KEY-INPUT. + ; so this code is never normally executed + ; when the keyboard is used for input. + + INC BC ; if it was AT/TAB - 3 locations required + BIT 7,(IY+$37) ; test FLAGX - Is this INPUT LINE ? + JP Z,L101E ; jump to ED-IGNORE if not, else + + CALL L15D4 ; routine WAIT-KEY - input address is KEY-NEXT + ; but is reset to KEY-INPUT + LD E,A ; save first in E + +;; ED-CONTR +L0F6C: CALL L15D4 ; routine WAIT-KEY for control. + ; input address will be key-next. + + PUSH DE ; saved code/parameters + LD HL,($5C5B) ; fetch address of keyboard cursor from K_CUR + RES 0,(IY+$07) ; set MODE to 'L' + + CALL L1655 ; routine MAKE-ROOM makes 2/3 spaces at cursor + + POP BC ; restore code/parameters + INC HL ; address first location + LD (HL),B ; place code (ink etc.) + INC HL ; address next + LD (HL),C ; place possible parameter. If only one + ; then DE points to this location also. + JR L0F8B ; forward to ADD-CH-1 + +; ------------------------ +; Add code to current line +; ------------------------ +; this is the branch used to add normal non-control characters +; with ED-LOOP as the stacked return address. +; it is also the OUTPUT service routine for system channel 'R'. + +;; ADD-CHAR +L0F81: RES 0,(IY+$07) ; set MODE to 'L' + +X0F85: LD HL,($5C5B) ; fetch address of keyboard cursor from K_CUR + CALL L1652 ; routine ONE-SPACE creates one space. + +; either a continuation of above or from ED-CONTR with ED-LOOP on stack. + +;; ADD-CH-1 +L0F8B: LD (DE),A ; load current character to last new location. + INC DE ; address next + LD ($5C5B),DE ; and update K_CUR system variable. + RET ; return - either a simple return + ; from ADD-CHAR or to ED-LOOP on stack. + +; --- + +; a branch of the editing loop to deal with control characters +; using a look-up table. + +;; ED-KEYS +L0F92: LD E,A ; character to E. + LD D,$00 ; prepare to add. + LD HL,L0FA0 - 7 ; base address of editing keys table. $0F99 + ADD HL,DE ; add E + LD E,(HL) ; fetch offset to E + ADD HL,DE ; add offset for address of handling routine. + PUSH HL ; push the address on machine stack. + LD HL,($5C5B) ; load address of cursor from K_CUR. + RET ; an make an indirect jump forward to routine. + +; ------------------ +; Editing keys table +; ------------------ +; For each code in the range $07 to $0F this table contains a +; single offset byte to the routine that services that code. +; Note. for what was intended there should also have been an +; entry for chr$ 6 with offset to ed-symbol. + +;; ed-keys-t +L0FA0: DEFB L0FA9 - $ ; 07d offset $09 to Address: ED-EDIT + DEFB L1007 - $ ; 08d offset $66 to Address: ED-LEFT + DEFB L100C - $ ; 09d offset $6A to Address: ED-RIGHT + DEFB L0FF3 - $ ; 10d offset $50 to Address: ED-DOWN + DEFB L1059 - $ ; 11d offset $B5 to Address: ED-UP + DEFB L1015 - $ ; 12d offset $70 to Address: ED-DELETE + DEFB L1024 - $ ; 13d offset $7E to Address: ED-ENTER + DEFB L1076 - $ ; 14d offset $CF to Address: ED-SYMBOL + DEFB L107C - $ ; 15d offset $D4 to Address: ED-GRAPH + +; --------------- +; Handle EDIT key +; --------------- +; The user has pressed SHIFT 1 to bring edit line down to bottom of screen. +; Alternatively the user wishes to clear the input buffer and start again. +; Alternatively ... + +;; ED-EDIT +L0FA9: LD HL,($5C49) ; fetch E_PPC the last line number entered. + ; Note. may not exist and may follow program. + BIT 5,(IY+$37) ; test FLAGX - input mode ? + JP NZ,L1097 ; jump forward to CLEAR-SP if not in editor. + + CALL L196E ; routine LINE-ADDR to find address of line + ; or following line if it doesn't exist. + CALL L1695 ; routine LINE-NO will get line number from + ; address or previous line if at end-marker. + LD A,D ; if there is no program then DE will + OR E ; contain zero so test for this. + JP Z,L1097 ; jump to to CLEAR-SP if so. + +; Note. at this point we have a validated line number, not just an +; approximation and it would be best to update E_PPC with the true +; cursor line value which would enable the line cursor to be suppressed +; in all situations - see shortly. + + PUSH HL ; save address of line. + INC HL ; address low byte of length. + LD C,(HL) ; transfer to C + INC HL ; next to high byte + LD B,(HL) ; transfer to B. + LD HL,$000A ; an overhead of ten bytes + ADD HL,BC ; is added to length. + LD B,H ; transfer adjusted value + LD C,L ; to BC register. + CALL L1F05 ; routine TEST-ROOM checks free memory. + CALL L1097 ; routine CLEAR-SP clears editing area. + LD HL,($5C51) ; address CURCHL + EX (SP),HL ; swap with line address on stack + PUSH HL ; save line address underneath + + LD A,$FF ; select system channel 'R' + CALL L1601 ; routine CHAN-OPEN opens it + + POP HL ; drop line address + DEC HL ; make it point to first byte of line num. + DEC (IY+$0F) ; decrease E_PPC_lo to suppress line cursor. + ; Note. ineffective when E_PPC is one + ; greater than last line of program perhaps + ; as a result of a delete. + ; credit. Paul Harrison 1982. + + CALL L1855 ; routine OUT-LINE outputs the BASIC line + ; to the editing area. + INC (IY+$0F) ; restore E_PPC_lo to the previous value. + LD HL,($5C59) ; address E_LINE in editing area. + INC HL ; advance + INC HL ; past space + INC HL ; and digit characters + INC HL ; of line number. + + LD ($5C5B),HL ; update K_CUR to address start of BASIC. + POP HL ; restore the address of CURCHL. + CALL L1615 ; routine CHAN-FLAG sets flags for it. + RET ; RETURN to ED-LOOP. + +; ------------------- +; Cursor down editing +; ------------------- +; The BASIC lines are displayed at the top of the screen and the user +; wishes to move the cursor down one line in edit mode. +; In input mode this key can be used as an alternative to entering STOP. + +;; ED-DOWN +L0FF3: BIT 5,(IY+$37) ; test FLAGX - Input Mode ? + JR NZ,L1001 ; skip to ED-STOP if so + + LD HL,$5C49 ; address E_PPC - 'current line' + CALL L190F ; routine LN-FETCH fetches number of next + ; line or same if at end of program. + JR L106E ; forward to ED-LIST to produce an + ; automatic listing. + +; --- + +;; ED-STOP +L1001: LD (IY+$00),$10 ; set ERR_NR to 'STOP in INPUT' code + JR L1024 ; forward to ED-ENTER to produce error. + +; ------------------- +; Cursor left editing +; ------------------- +; This acts on the cursor in the lower section of the screen in both +; editing and input mode. + +;; ED-LEFT +L1007: CALL L1031 ; routine ED-EDGE moves left if possible + JR L1011 ; forward to ED-CUR to update K-CUR + ; and return to ED-LOOP. + +; -------------------- +; Cursor right editing +; -------------------- +; This acts on the cursor in the lower screen in both editing and input +; mode and moves it to the right. + +;; ED-RIGHT +L100C: LD A,(HL) ; fetch addressed character. + CP $0D ; is it carriage return ? + RET Z ; return if so to ED-LOOP + + INC HL ; address next character + +;; ED-CUR +L1011: LD ($5C5B),HL ; update K_CUR system variable + RET ; return to ED-LOOP + +; -------------- +; DELETE editing +; -------------- +; This acts on the lower screen and deletes the character to left of +; cursor. If control characters are present these are deleted first +; leaving the naked parameter (0-7) which appears as a '?' except in the +; case of chr$ 6 which is the comma control character. It is not mandatory +; to delete these second characters. + +;; ED-DELETE +L1015: CALL L1031 ; routine ED-EDGE moves cursor to left. + LD BC,$0001 ; of character to be deleted. + JP L19E8 ; to RECLAIM-2 reclaim the character. + +; ------------------------------------------ +; Ignore next 2 codes from key-input routine +; ------------------------------------------ +; Since AT and TAB cannot be entered this point is never reached +; from the keyboard. If inputting from a tape device or network then +; the control and two following characters are ignored and processing +; continues as if a carriage return had been received. +; Here, perhaps, another Spectrum has said print #15; AT 0,0; "This is yellow" +; and this one is interpreting input #15; a$. + +;; ED-IGNORE +L101E: CALL L15D4 ; routine WAIT-KEY to ignore keystroke. + CALL L15D4 ; routine WAIT-KEY to ignore next key. + +; ------------- +; Enter/newline +; ------------- +; The enter key has been pressed to have BASIC line or input accepted. + +;; ED-ENTER +L1024: POP HL ; discard address ED-LOOP + POP HL ; drop address ED-ERROR + +;; ED-END +L1026: POP HL ; the previous value of ERR_SP + LD ($5C3D),HL ; is restored to ERR_SP system variable + BIT 7,(IY+$00) ; is ERR_NR $FF (= 'OK') ? + RET NZ ; return if so + + LD SP,HL ; else put error routine on stack + RET ; and make an indirect jump to it. + +; ----------------------------- +; Move cursor left when editing +; ----------------------------- +; This routine moves the cursor left. The complication is that it must +; not position the cursor between control codes and their parameters. +; It is further complicated in that it deals with TAB and AT characters +; which are never present from the keyboard. +; The method is to advance from the beginning of the line each time, +; jumping one, two, or three characters as necessary saving the original +; position at each jump in DE. Once it arrives at the cursor then the next +; legitimate leftmost position is in DE. + +;; ED-EDGE +L1031: SCF ; carry flag must be set to call the nested + CALL L1195 ; subroutine SET-DE. + ; if input then DE=WORKSP + ; if editing then DE=E_LINE + SBC HL,DE ; subtract address from start of line + ADD HL,DE ; and add back. + INC HL ; adjust for carry. + POP BC ; drop return address + RET C ; return to ED-LOOP if already at left + ; of line. + + PUSH BC ; resave return address - ED-LOOP. + LD B,H ; transfer HL - cursor address + LD C,L ; to BC register pair. + ; at this point DE addresses start of line. + +;; ED-EDGE-1 +L103E: LD H,D ; transfer DE - leftmost pointer + LD L,E ; to HL + INC HL ; address next leftmost character to + ; advance position each time. + LD A,(DE) ; pick up previous in A + AND $F0 ; lose the low bits + CP $10 ; is it INK to TAB $10-$1F ? + ; that is, is it followed by a parameter ? + JR NZ,L1051 ; to ED-EDGE-2 if not + ; HL has been incremented once + + INC HL ; address next as at least one parameter. + +; in fact since 'tab' and 'at' cannot be entered the next section seems +; superfluous. +; The test will always fail and the jump to ED-EDGE-2 will be taken. + + LD A,(DE) ; reload leftmost character + SUB $17 ; decimal 23 ('tab') + ADC A,$00 ; will be 0 for 'tab' and 'at'. + JR NZ,L1051 ; forward to ED-EDGE-2 if not + ; HL has been incremented twice + + INC HL ; increment a third time for 'at'/'tab' + +;; ED-EDGE-2 +L1051: AND A ; prepare for true subtraction + SBC HL,BC ; subtract cursor address from pointer + ADD HL,BC ; and add back + ; Note when HL matches the cursor position BC, + ; there is no carry and the previous + ; position is in DE. + EX DE,HL ; transfer result to DE if looping again. + ; transfer DE to HL to be used as K-CUR + ; if exiting loop. + JR C,L103E ; back to ED-EDGE-1 if cursor not matched. + + RET ; return. + +; ----------------- +; Cursor up editing +; ----------------- +; The main screen displays part of the BASIC program and the user wishes +; to move up one line scrolling if necessary. +; This has no alternative use in input mode. + +;; ED-UP +L1059: BIT 5,(IY+$37) ; test FLAGX - input mode ? + RET NZ ; return if not in editor - to ED-LOOP. + + LD HL,($5C49) ; get current line from E_PPC + CALL L196E ; routine LINE-ADDR gets address + EX DE,HL ; and previous in DE + CALL L1695 ; routine LINE-NO gets prev line number + LD HL,$5C4A ; set HL to E_PPC_hi as next routine stores + ; top first. + CALL L191C ; routine LN-STORE loads DE value to HL + ; high byte first - E_PPC_lo takes E + +; this branch is also taken from ed-down. + +;; ED-LIST +L106E: CALL L1795 ; routine AUTO-LIST lists to upper screen + ; including adjusted current line. + LD A,$00 ; select lower screen again + JP L1601 ; exit via CHAN-OPEN to ED-LOOP + +; -------------------------------- +; Use of symbol and graphics codes +; -------------------------------- +; These will not be encountered with the keyboard but would be handled +; otherwise as follows. +; As noted earlier, Vickers says there should have been an entry in +; the KEYS table for chr$ 6 which also pointed here. +; If, for simplicity, two Spectrums were both using #15 as a bi-directional +; channel connected to each other:- +; then when the other Spectrum has said PRINT #15; x, y +; input #15; i ; j would treat the comma control as a newline and the +; control would skip to input j. +; You can get round the missing chr$ 6 handler by sending multiple print +; items separated by a newline '. + +; chr$14 would have the same functionality. + +; This is chr$ 14. +;; ED-SYMBOL +L1076: BIT 7,(IY+$37) ; test FLAGX - is this INPUT LINE ? + JR Z,L1024 ; back to ED-ENTER if not to treat as if + ; enter had been pressed. + ; else continue and add code to buffer. + +; Next is chr$ 15 +; Note that ADD-CHAR precedes the table so we can't offset to it directly. + +;; ED-GRAPH +L107C: JP L0F81 ; jump back to ADD-CHAR + +; -------------------- +; Editor error routine +; -------------------- +; If an error occurs while editing, or inputting, then ERR_SP +; points to the stack location holding address ED_ERROR. + +;; ED-ERROR +L107F: BIT 4,(IY+$30) ; test FLAGS2 - is K channel in use ? + JR Z,L1026 ; back to ED-END if not. + +; but as long as we're editing lines or inputting from the keyboard, then +; we've run out of memory so give a short rasp. + + LD (IY+$00),$FF ; reset ERR_NR to 'OK'. + LD D,$00 ; prepare for beeper. + LD E,(IY-$02) ; use RASP value. + LD HL,$1A90 ; set a duration. + CALL L03B5 ; routine BEEPER emits a warning rasp. + JP L0F30 ; to ED-AGAIN to re-stack address of + ; this routine and make ERR_SP point to it. + +; --------------------- +; Clear edit/work space +; --------------------- +; The editing area or workspace is cleared depending on context. +; This is called from ED-EDIT to clear workspace if edit key is +; used during input, to clear editing area if no program exists +; and to clear editing area prior to copying the edit line to it. +; It is also used by the error routine to clear the respective +; area depending on FLAGX. + +;; CLEAR-SP +L1097: PUSH HL ; preserve HL + CALL L1190 ; routine SET-HL + ; if in edit HL = WORKSP-1, DE = E_LINE + ; if in input HL = STKBOT, DE = WORKSP + DEC HL ; adjust + CALL L19E5 ; routine RECLAIM-1 reclaims space + LD ($5C5B),HL ; set K_CUR to start of empty area + LD (IY+$07),$00 ; set MODE to 'KLC' + POP HL ; restore HL. + RET ; return. + +; --------------------- +; Handle keyboard input +; --------------------- +; This is the service routine for the input stream of the keyboard +; channel 'K'. + +;; KEY-INPUT +L10A8: BIT 3,(IY+$02) ; test TV_FLAG - has a key been pressed in + ; editor ? + CALL NZ,L111D ; routine ED-COPY if so to reprint the lower + ; screen at every keystroke. + AND A ; clear carry - required exit condition. + BIT 5,(IY+$01) ; test FLAGS - has a new key been pressed ? + RET Z ; return if not. + + LD A,($5C08) ; system variable LASTK will hold last key - + ; from the interrupt routine. + RES 5,(IY+$01) ; update FLAGS - reset the new key flag. + PUSH AF ; save the input character. + BIT 5,(IY+$02) ; test TV_FLAG - clear lower screen ? + CALL NZ,L0D6E ; routine CLS-LOWER if so. + + POP AF ; restore the character code. + CP $20 ; if space or higher then + JR NC,L111B ; forward to KEY-DONE2 and return with carry + ; set to signal key-found. + + CP $10 ; with 16d INK and higher skip + JR NC,L10FA ; forward to KEY-CONTR. + + CP $06 ; for 6 - 15d + JR NC,L10DB ; skip forward to KEY-M-CL to handle Modes + ; and CapsLock. + +; that only leaves 0-5, the flash bright inverse switches. + + LD B,A ; save character in B + AND $01 ; isolate the embedded parameter (0/1). + LD C,A ; and store in C + LD A,B ; re-fetch copy (0-5) + RRA ; halve it 0, 1 or 2. + ADD A,$12 ; add 18d gives 'flash', 'bright' + ; and 'inverse'. + JR L1105 ; forward to KEY-DATA with the + ; parameter (0/1) in C. + +; --- + +; Now separate capslock 06 from modes 7-15. + +;; KEY-M-CL +L10DB: JR NZ,L10E6 ; forward to KEY-MODE if not 06 (capslock) + + LD HL,$5C6A ; point to FLAGS2 + LD A,$08 ; value 00000100 + XOR (HL) ; toggle BIT 2 of FLAGS2 the capslock bit + LD (HL),A ; and store result in FLAGS2 again. + JR L10F4 ; forward to KEY-FLAG to signal no-key. + +; --- + +;; KEY-MODE +L10E6: CP $0E ; compare with chr 14d + RET C ; return with carry set "key found" for + ; codes 7 - 13d leaving 14d and 15d + ; which are converted to mode codes. + + SUB $0D ; subtract 13d leaving 1 and 2 + ; 1 is 'E' mode, 2 is 'G' mode. + LD HL,$5C41 ; address the MODE system variable. + CP (HL) ; compare with existing value before + LD (HL),A ; inserting the new value. + JR NZ,L10F4 ; forward to KEY-FLAG if it has changed. + + LD (HL),$00 ; else make MODE zero - KLC mode + ; Note. while in Extended/Graphics mode, + ; the Extended Mode/Graphics key is pressed + ; again to get out. + +;; KEY-FLAG +L10F4: SET 3,(IY+$02) ; update TV_FLAG - show key state has changed + CP A ; clear carry and reset zero flags - + ; no actual key returned. + RET ; make the return. + +; --- + +; now deal with colour controls - 16-23 ink, 24-31 paper + +;; KEY-CONTR +L10FA: LD B,A ; make a copy of character. + AND $07 ; mask to leave bits 0-7 + LD C,A ; and store in C. + LD A,$10 ; initialize to 16d - INK. + BIT 3,B ; was it paper ? + JR NZ,L1105 ; forward to KEY-DATA with INK 16d and + ; colour in C. + + INC A ; else change from INK to PAPER (17d) if so. + +;; KEY-DATA +L1105: LD (IY-$2D),C ; put the colour (0-7)/state(0/1) in KDATA + LD DE,L110D ; address: KEY-NEXT will be next input stream + JR L1113 ; forward to KEY-CHAN to change it ... + +; --- + +; ... so that INPUT_AD directs control to here at next call to WAIT-KEY + +;; KEY-NEXT +L110D: LD A,($5C0D) ; pick up the parameter stored in KDATA. + LD DE,L10A8 ; address: KEY-INPUT will be next input stream + ; continue to restore default channel and + ; make a return with the control code. + +;; KEY-CHAN +L1113: LD HL,($5C4F) ; address start of CHANNELS area using CHANS + ; system variable. + ; Note. One might have expected CURCHL to + ; have been used. + INC HL ; step over the + INC HL ; output address + LD (HL),E ; and update the input + INC HL ; routine address for + LD (HL),D ; the next call to WAIT-KEY. + +;; KEY-DONE2 +L111B: SCF ; set carry flag to show a key has been found + RET ; and return. + +; -------------------- +; Lower screen copying +; -------------------- +; This subroutine is called whenever the line in the editing area or +; input workspace is required to be printed to the lower screen. +; It is by calling this routine after any change that the cursor, for +; instance, appears to move to the left. +; Remember the edit line will contain characters and tokens +; e.g. "1000 LET a = 1" is 12 characters. + +;; ED-COPY +L111D: CALL L0D4D ; routine TEMPS sets temporary attributes. + RES 3,(IY+$02) ; update TV_FLAG - signal no change in mode + RES 5,(IY+$02) ; update TV_FLAG - signal don't clear lower + ; screen. + LD HL,($5C8A) ; fetch SPOSNL + PUSH HL ; and save on stack. + + LD HL,($5C3D) ; fetch ERR_SP + PUSH HL ; and save also + LD HL,L1167 ; address: ED-FULL + PUSH HL ; is pushed as the error routine + LD ($5C3D),SP ; and ERR_SP made to point to it. + + LD HL,($5C82) ; fetch ECHO_E + PUSH HL ; and push also + + SCF ; set carry flag to control SET-DE + CALL L1195 ; call routine SET-DE + ; if in input DE = WORKSP + ; if in edit DE = E_LINE + EX DE,HL ; start address to HL + + CALL L187D ; routine OUT-LINE2 outputs entire line up to + ; carriage return including initial + ; characterized line number when present. + EX DE,HL ; transfer new address to DE + CALL L18E1 ; routine OUT-CURS considers a + ; terminating cursor. + + LD HL,($5C8A) ; fetch updated SPOSNL + EX (SP),HL ; exchange with ECHO_E on stack + EX DE,HL ; transfer ECHO_E to DE + CALL L0D4D ; routine TEMPS to re-set attributes + ; if altered. + +; the lower screen was not cleared, at the outset, so if deleting then old +; text from a previous print may follow this line and requires blanking. + +;; ED-BLANK +L1150: LD A,($5C8B) ; fetch SPOSNL_hi is current line + SUB D ; compare with old + JR C,L117C ; forward to ED-C-DONE if no blanking + + JR NZ,L115E ; forward to ED-SPACES if line has changed + + LD A,E ; old column to A + SUB (IY+$50) ; subtract new in SPOSNL_lo + JR NC,L117C ; forward to ED-C-DONE if no backfilling. + +;; ED-SPACES +L115E: LD A,$20 ; prepare a space. + PUSH DE ; save old line/column. + CALL L09F4 ; routine PRINT-OUT prints a space over + ; any text from previous print. + ; Note. Since the blanking only occurs when + ; using $09F4 to print to the lower screen, + ; there is no need to vector via a RST 10 + ; and we can use this alternate set. + POP DE ; restore the old line column. + JR L1150 ; back to ED-BLANK until all old text blanked. + +; ------- +; ED-FULL +; ------- +; this is the error routine addressed by ERR_SP. This is not for the out of +; memory situation as we're just printing. The pitch and duration are exactly +; the same as used by ED-ERROR from which this has been augmented. The +; situation is that the lower screen is full and a rasp is given to suggest +; that this is perhaps not the best idea you've had that day. + +;; ED-FULL +L1167: LD D,$00 ; prepare to moan. + LD E,(IY-$02) ; fetch RASP value. + LD HL,$1A90 ; set duration. + CALL L03B5 ; routine BEEPER. + LD (IY+$00),$FF ; clear ERR_NR. + LD DE,($5C8A) ; fetch SPOSNL. + JR L117E ; forward to ED-C-END + +; ------- + +; the exit point from line printing continues here. + +;; ED-C-DONE +L117C: POP DE ; fetch new line/column. + POP HL ; fetch the error address. + +; the error path rejoins here. + +;; ED-C-END +L117E: POP HL ; restore the old value of ERR_SP. + LD ($5C3D),HL ; update the system variable ERR_SP + POP BC ; old value of SPOSN_L + PUSH DE ; save new value + CALL L0DD9 ; routine CL-SET and PO-STORE + ; update ECHO_E and SPOSN_L from BC + POP HL ; restore new value + LD ($5C82),HL ; and update ECHO_E + LD (IY+$26),$00 ; make error pointer X_PTR_hi out of bounds + RET ; return + +; ----------------------------------------------- +; Point to first and last locations of work space +; ----------------------------------------------- +; These two nested routines ensure that the appropriate pointers are +; selected for the editing area or workspace. The routines that call +; these routines are designed to work on either area. + +; this routine is called once +;; SET-HL +L1190: LD HL,($5C61) ; fetch WORKSP to HL. + DEC HL ; point to last location of editing area. + AND A ; clear carry to limit exit points to first + ; or last. + +; this routine is called with carry set and exits at a conditional return. + +;; SET-DE +L1195: LD DE,($5C59) ; fetch E_LINE to DE + BIT 5,(IY+$37) ; test FLAGX - Input Mode ? + RET Z ; return now if in editing mode + + LD DE,($5C61) ; fetch WORKSP to DE + RET C ; return if carry set ( entry = set-de) + + LD HL,($5C63) ; fetch STKBOT to HL as well + RET ; and return (entry = set-hl (in input)) + +; ------------------------------- +; Remove floating point from line +; ------------------------------- +; When a BASIC LINE or the INPUT BUFFER is parsed any numbers will have +; an invisible chr 14d inserted after them and the 5-byte integer or +; floating point form inserted after that. Similar invisible value holders +; are also created after the numeric and string variables in a DEF FN list. +; This routine removes these 'compiled' numbers from the edit line or +; input workspace. + +;; REMOVE-FP +L11A7: LD A,(HL) ; fetch character + CP $0E ; is it the number marker ? + LD BC,$0006 ; prepare for six bytes + CALL Z,L19E8 ; routine RECLAIM-2 reclaims space if $0E + LD A,(HL) ; reload next (or same) character + INC HL ; and advance address + CP $0D ; end of line or input buffer ? + JR NZ,L11A7 ; back to REMOVE-FP until entire line done. + + RET ; return + + +;********************************* +;** Part 6. EXECUTIVE ROUTINES ** +;********************************* + + +; The memory. +; +; +---------+-----------+------------+--------------+-------------+-- +; | BASIC | Display | Attributes | ZX Printer | System | +; | ROM | File | File | Buffer | Variables | +; +---------+-----------+------------+--------------+-------------+-- +; ^ ^ ^ ^ ^ ^ +; $0000 $4000 $5800 $5B00 $5C00 $5CB6 = CHANS +; +; +; --+----------+---+---------+-----------+---+------------+--+---+-- +; | Channel |$80| BASIC | Variables |$80| Edit Line |NL|$80| +; | Info | | Program | Area | | or Command | | | +; --+----------+---+---------+-----------+---+------------+--+---+-- +; ^ ^ ^ ^ ^ +; CHANS PROG VARS E_LINE WORKSP +; +; +; ---5--> <---2--- <--3--- +; --+-------+--+------------+-------+-------+---------+-------+-+---+------+ +; | INPUT |NL| Temporary | Calc. | Spare | Machine | GOSUB |?|$3E| UDGs | +; | data | | Work Space | Stack | | Stack | Stack | | | | +; --+-------+--+------------+-------+-------+---------+-------+-+---+------+ +; ^ ^ ^ ^ ^ ^ ^ +; WORKSP STKBOT STKEND sp RAMTOP UDG P_RAMT +; + +; ------------------- +; Handle NEW command +; ------------------- +; The NEW command is about to set all RAM below RAMTOP to zero and +; then re-initialize the system. All RAM above RAMTOP should, and will be, +; preserved. +; There is nowhere to store values in RAM or on the stack which becomes +; inoperable. Similarly PUSH and CALL instructions cannot be used to +; store values or section common code. The alternate register set is the only +; place available to store 3 persistent 16-bit system variables. + +;; NEW +L11B7: DI ; disable interrupts - machine stack will be + ; cleared. + LD A,$FF ; flag coming from NEW. + LD DE,($5CB2) ; fetch RAMTOP as top value. + EXX ; switch in alternate set. + LD BC,($5CB4) ; fetch P-RAMT differs on 16K/48K machines. + LD DE,($5C38) ; fetch RASP/PIP. + LD HL,($5C7B) ; fetch UDG differs on 16K/48K machines. + EXX ; switch back to main set and continue into... + +; --------------------------- +; Main entry (initialization) +; --------------------------- +; This common code tests ram and sets it to zero re-initializing +; all the non-zero system variables and channel information. +; The A register tells if coming from START or NEW + +;; START-NEW +L11CB: LD B,A ; save the flag for later branching. + + LD A,$07 ; select a white border + OUT ($FE),A ; and set it now. + + LD A,$3F ; load accumulator with last page in ROM. + LD I,A ; set the I register - this remains constant + ; and can't be in range $40 - $7F as 'snow' + ; appears on the screen. + NOP ; these seem unnecessary. + NOP ; + NOP ; + NOP ; + NOP ; + NOP ; + +; ------------ +; Check RAM +; ------------ +; Typically a Spectrum will have 16K or 48K of Ram and this code will +; test it all till it finds an unpopulated location or, less likely, a +; faulty location. Usually it stops when it reaches the top $FFFF or +; in the case of NEW the supplied top value. The entire screen turns +; black with sometimes red stripes on black paper visible. + +;; ram-check +L11DA: LD H,D ; transfer the top value to + LD L,E ; the HL register pair. + +;; RAM-FILL +L11DC: LD (HL),$02 ; load with 2 - red ink on black paper + DEC HL ; next lower + CP H ; have we reached ROM - $3F ? + JR NZ,L11DC ; back to RAM-FILL if not. + +;; RAM-READ +L11E2: AND A ; clear carry - prepare to subtract + SBC HL,DE ; subtract and add back setting + ADD HL,DE ; carry when back at start. + INC HL ; and increment for next iteration. + JR NC,L11EF ; forward to RAM-DONE if we've got back to + ; starting point with no errors. + + DEC (HL) ; decrement to 1. + JR Z,L11EF ; forward to RAM-DONE if faulty. + + DEC (HL) ; decrement to zero. + JR Z,L11E2 ; back to RAM-READ if zero flag was set. + +;; RAM-DONE +L11EF: DEC HL ; step back to last valid location. + EXX ; regardless of state, set up possibly + ; stored system variables in case from NEW. + LD ($5CB4),BC ; insert P-RAMT. + LD ($5C38),DE ; insert RASP/PIP. + LD ($5C7B),HL ; insert UDG. + EXX ; switch in main set. + INC B ; now test if we arrived here from NEW. + JR Z,L1219 ; forward to RAM-SET if we did. + +; this section applies to START only. + + LD ($5CB4),HL ; set P-RAMT to the highest working RAM + ; address. + LD DE,$3EAF ; address of last byte of 'U' bitmap in ROM. + LD BC,$00A8 ; there are 21 user defined graphics. + EX DE,HL ; switch pointers and make the UDGs a + LDDR ; copy of the standard characters A - U. + EX DE,HL ; switch the pointer to HL. + INC HL ; update to start of 'A' in RAM. + LD ($5C7B),HL ; make UDG system variable address the first + ; bitmap. + DEC HL ; point at RAMTOP again. + + LD BC,$0040 ; set the values of + LD ($5C38),BC ; the PIP and RASP system variables. + +; the NEW command path rejoins here. + +;; RAM-SET +L1219: LD ($5CB2),HL ; set system variable RAMTOP to HL. + + LD HL,$3C00 ; a strange place to set the pointer to the + LD ($5C36),HL ; character set, CHARS - as no printing yet. + + LD HL,($5CB2) ; fetch RAMTOP to HL again as we've lost it. + + LD (HL),$3E ; top of user ram holds GOSUB end marker + ; an impossible line number - see RETURN. + ; no significance in the number $3E. It has + ; been traditional since the ZX80. + + DEC HL ; followed by empty byte (not important). + LD SP,HL ; set up the machine stack pointer. + DEC HL ; + DEC HL ; + LD ($5C3D),HL ; ERR_SP is where the error pointer is + ; at moment empty - will take address MAIN-4 + ; at the call preceding that address, + ; although interrupts and calls will make use + ; of this location in meantime. + + IM 1 ; select interrupt mode 1. + LD IY,$5C3A ; set IY to ERR_NR. IY can reach all standard + ; system variables but shadow ROM system + ; variables will be mostly out of range. + + EI ; enable interrupts now that we have a stack. + + LD HL,$5CB6 ; the address of the channels - initially + ; following system variables. + LD ($5C4F),HL ; set the CHANS system variable. + + LD DE,L15AF ; address: init-chan in ROM. + LD BC,$0015 ; there are 21 bytes of initial data in ROM. + EX DE,HL ; swap the pointers. + LDIR ; copy the bytes to RAM. + + EX DE,HL ; swap pointers. HL points to program area. + DEC HL ; decrement address. + LD ($5C57),HL ; set DATADD to location before program area. + INC HL ; increment again. + + LD ($5C53),HL ; set PROG the location where BASIC starts. + LD ($5C4B),HL ; set VARS to same location with a + LD (HL),$80 ; variables end-marker. + INC HL ; advance address. + LD ($5C59),HL ; set E_LINE, where the edit line + ; will be created. + ; Note. it is not strictly necessary to + ; execute the next fifteen bytes of code + ; as this will be done by the call to SET-MIN. + ; -- + LD (HL),$0D ; initially just has a carriage return + INC HL ; followed by + LD (HL),$80 ; an end-marker. + INC HL ; address the next location. + LD ($5C61),HL ; set WORKSP - empty workspace. + LD ($5C63),HL ; set STKBOT - bottom of the empty stack. + LD ($5C65),HL ; set STKEND to the end of the empty stack. + ; -- + LD A,$38 ; the colour system is set to white paper, + ; black ink, no flash or bright. + LD ($5C8D),A ; set ATTR_P permanent colour attributes. + LD ($5C8F),A ; set ATTR_T temporary colour attributes. + LD ($5C48),A ; set BORDCR the border colour/lower screen + ; attributes. + + LD HL,$0523 ; The keyboard repeat and delay values + LD ($5C09),HL ; are loaded to REPDEL and REPPER. + + DEC (IY-$3A) ; set KSTATE-0 to $FF. + DEC (IY-$36) ; set KSTATE-4 to $FF. + ; thereby marking both available. + + LD HL,L15C6 ; set source to ROM Address: init-strm + LD DE,$5C10 ; set destination to system variable STRMS-FD + LD BC,$000E ; copy the 14 bytes of initial 7 streams data + LDIR ; from ROM to RAM. + + SET 1,(IY+$01) ; update FLAGS - signal printer in use. + CALL L0EDF ; call routine CLEAR-PRB to initialize system + ; variables associated with printer. + + LD (IY+$31),$02 ; set DF_SZ the lower screen display size to + ; two lines + CALL L0D6B ; call routine CLS to set up system + ; variables associated with screen and clear + ; the screen and set attributes. + XOR A ; clear accumulator so that we can address + LD DE,L1539 - 1 ; the message table directly. + CALL L0C0A ; routine PO-MSG puts + ; '(c) 1982 Sinclair Research Ltd' + ; at bottom of display. + SET 5,(IY+$02) ; update TV_FLAG - signal lower screen will + ; require clearing. + + JR L12A9 ; forward to MAIN-1 + +; ------------------- +; Main execution loop +; ------------------- +; +; + +;; MAIN-EXEC +L12A2: LD (IY+$31),$02 ; set DF_SZ lower screen display file + ; size to 2 lines. + CALL L1795 ; routine AUTO-LIST + +;; MAIN-1 +L12A9: CALL L16B0 ; routine SET-MIN clears work areas. + +;; MAIN-2 +L12AC: LD A,$00 ; select channel 'K' the keyboard + CALL L1601 ; routine CHAN-OPEN opens it + CALL L0F2C ; routine EDITOR is called. + ; Note the above routine is where the Spectrum + ; waits for user-interaction. Perhaps the + ; most common input at this stage + ; is LOAD "". + CALL L1B17 ; routine LINE-SCAN scans the input. + BIT 7,(IY+$00) ; test ERR_NR - will be $FF if syntax + ; is correct. + JR NZ,L12CF ; forward, if correct, to MAIN-3. + +; + + BIT 4,(IY+$30) ; test FLAGS2 - K channel in use ? + JR Z,L1303 ; forward to MAIN-4 if not. + +; + + LD HL,($5C59) ; an editing error so address E_LINE. + CALL L11A7 ; routine REMOVE-FP removes the hidden + ; floating-point forms. + LD (IY+$00),$FF ; system variable ERR_NR is reset to 'OK'. + JR L12AC ; back to MAIN-2 to allow user to correct. + +; --- + +; the branch was here if syntax has passed test. + +;; MAIN-3 +L12CF: LD HL,($5C59) ; fetch the edit line address from E_LINE. + LD ($5C5D),HL ; system variable CH_ADD is set to first + ; character of edit line. + ; Note. the above two instructions are a little + ; inadequate. + ; They are repeated with a subtle difference + ; at the start of the next subroutine and are + ; therefore not required above. + + CALL L19FB ; routine E-LINE-NO will fetch any line + ; number to BC if this is a program line. + + LD A,B ; test if the number of + OR C ; the line is non-zero. + JP NZ,L155D ; jump forward to MAIN-ADD if so to add the + ; line to the BASIC program. + +; Has the user just pressed the ENTER key ? + + RST 18H ; GET-CHAR gets character addressed by CH_ADD. + CP $0D ; is it a carriage return ? + JR Z,L12A2 ; back to MAIN-EXEC if so for an automatic + ; listing. + +; this must be a direct command. + + BIT 0,(IY+$30) ; test FLAGS2 - clear the main screen ? + CALL NZ,L0DAF ; routine CL-ALL, if so, e.g. after listing. + CALL L0D6E ; routine CLS-LOWER anyway. + LD A,$19 ; compute scroll count to 25 minus + SUB (IY+$4F) ; value of S_POSN_hi. + LD ($5C8C),A ; update SCR_CT system variable. + SET 7,(IY+$01) ; update FLAGS - signal running program. + LD (IY+$00),$FF ; set ERR_NR to 'OK'. + LD (IY+$0A),$01 ; set NSPPC to one for first statement. + CALL L1B8A ; call routine LINE-RUN to run the line. + ; sysvar ERR_SP therefore addresses MAIN-4 + +; Examples of direct commands are RUN, CLS, LOAD "", PRINT USR 40000, +; LPRINT "A"; etc.. +; If a user written machine-code program disables interrupts then it +; must enable them to pass the next step. We also jumped to here if the +; keyboard was not being used. + +;; MAIN-4 +L1303: HALT ; wait for interrupt. + + RES 5,(IY+$01) ; update FLAGS - signal no new key. + BIT 1,(IY+$30) ; test FLAGS2 - is printer buffer clear ? + CALL NZ,L0ECD ; call routine COPY-BUFF if not. + ; Note. the programmer has neglected + ; to set bit 1 of FLAGS first. + + LD A,($5C3A) ; fetch ERR_NR + INC A ; increment to give true code. + +; Now deal with a runtime error as opposed to an editing error. +; However if the error code is now zero then the OK message will be printed. + +;; MAIN-G +L1313: PUSH AF ; save the error number. + + LD HL,$0000 ; prepare to clear some system variables. + LD (IY+$37),H ; clear all the bits of FLAGX. + LD (IY+$26),H ; blank X_PTR_hi to suppress error marker. + LD ($5C0B),HL ; blank DEFADD to signal that no defined + ; function is currently being evaluated. + + LD HL,$0001 ; explicit - inc hl would do. + LD ($5C16),HL ; ensure STRMS-00 is keyboard. + + CALL L16B0 ; routine SET-MIN clears workspace etc. + RES 5,(IY+$37) ; update FLAGX - signal in EDIT not INPUT mode. + ; Note. all the bits were reset earlier. + + CALL L0D6E ; call routine CLS-LOWER. + SET 5,(IY+$02) ; update TV_FLAG - signal lower screen + ; requires clearing. + + POP AF ; bring back the error number + LD B,A ; and make a copy in B. + CP $0A ; is it a print-ready digit ? + JR C,L133C ; forward to MAIN-5 if so. + + ADD A,$07 ; add ASCII offset to letters. + +;; MAIN-5 +L133C: CALL L15EF ; call routine OUT-CODE to print the code. + + LD A,$20 ; followed by a space. + RST 10H ; PRINT-A + + LD A,B ; fetch stored report code. + LD DE,L1391 ; address: rpt-mesgs. + CALL L0C0A ; call routine PO-MSG to print. + +X1349: CALL L3B3B ; Spectrum 128 patch + NOP + +L134D: CALL L0C0A ; routine PO-MSG prints them although it would + ; be more succinct to use RST $10. + + LD BC,($5C45) ; fetch PPC the current line number. + CALL L1A1B ; routine OUT-NUM-1 will print that + LD A,$3A ; then a ':'. + RST 10H ; PRINT-A + + LD C,(IY+$0D) ; then SUBPPC for statement + LD B,$00 ; limited to 127 + CALL L1A1B ; routine OUT-NUM-1 + + CALL L1097 ; routine CLEAR-SP clears editing area. + ; which probably contained 'RUN'. + LD A,($5C3A) ; fetch ERR_NR again + INC A ; test for no error originally $FF. + JR Z,L1386 ; forward to MAIN-9 if no error. + + CP $09 ; is code Report 9 STOP ? + JR Z,L1373 ; forward to MAIN-6 if so + + CP $15 ; is code Report L Break ? + JR NZ,L1376 ; forward to MAIN-7 if not + +; Stop or Break was encountered so consider CONTINUE. + +;; MAIN-6 +L1373: INC (IY+$0D) ; increment SUBPPC to next statement. + +;; MAIN-7 +L1376: LD BC,$0003 ; prepare to copy 3 system variables to + LD DE,$5C70 ; address OSPPC - statement for CONTINUE. + ; also updating OLDPPC line number below. + + LD HL,$5C44 ; set source top to NSPPC next statement. + BIT 7,(HL) ; did BREAK occur before the jump ? + ; e.g. between GO TO and next statement. + JR Z,L1384 ; skip forward to MAIN-8, if not, as setup + ; is correct. + + ADD HL,BC ; set source to SUBPPC number of current + ; statement/line which will be repeated. + +;; MAIN-8 +L1384: LDDR ; copy PPC to OLDPPC and SUBPPC to OSPCC + ; or NSPPC to OLDPPC and NEWPPC to OSPCC + +;; MAIN-9 +L1386: LD (IY+$0A),$FF ; update NSPPC - signal 'no jump'. + RES 3,(IY+$01) ; update FLAGS - signal use 'K' mode for + ; the first character in the editor and + JP L12AC ; jump back to MAIN-2. + + +; ---------------------- +; Canned report messages +; ---------------------- +; The Error reports with the last byte inverted. The first entry +; is a dummy entry. The last, which begins with $7F, the Spectrum +; character for copyright symbol, is placed here for convenience +; as is the preceding comma and space. +; The report line must accommodate a 4-digit line number and a 3-digit +; statement number which limits the length of the message text to twenty +; characters. +; e.g. "B Integer out of range, 1000:127" + +;; rpt-mesgs +L1391: DEFB $80 + DEFB 'O','K'+$80 ; 0 + DEFM "NEXT without FO" + DEFB 'R'+$80 ; 1 + DEFM "Variable not foun" + DEFB 'd'+$80 ; 2 + DEFM "Subscript wron" + DEFB 'g'+$80 ; 3 + DEFM "Out of memor" + DEFB 'y'+$80 ; 4 + DEFM "Out of scree" + DEFB 'n'+$80 ; 5 + DEFM "Number too bi" + DEFB 'g'+$80 ; 6 + DEFM "RETURN without GOSU" + DEFB 'B'+$80 ; 7 + DEFM "End of fil" + DEFB 'e'+$80 ; 8 + DEFM "STOP statemen" + DEFB 't'+$80 ; 9 + DEFM "Invalid argumen" + DEFB 't'+$80 ; A + DEFM "Integer out of rang" + DEFB 'e'+$80 ; B + DEFM "Nonsense in BASI" + DEFB 'C'+$80 ; C + DEFM "BREAK - CONT repeat" + DEFB 's'+$80 ; D + DEFM "Out of DAT" + DEFB 'A'+$80 ; E + DEFM "Invalid file nam" + DEFB 'e'+$80 ; F + DEFM "No room for lin" + DEFB 'e'+$80 ; G + DEFM "STOP in INPU" + DEFB 'T'+$80 ; H + DEFM "FOR without NEX" + DEFB 'T'+$80 ; I + DEFM "Invalid I/O devic" + DEFB 'e'+$80 ; J + DEFM "Invalid colou" + DEFB 'r'+$80 ; K + DEFM "BREAK into progra" + DEFB 'm'+$80 ; L + DEFM "RAMTOP no goo" + DEFB 'd'+$80 ; M + DEFM "Statement los" + DEFB 't'+$80 ; N + DEFM "Invalid strea" + DEFB 'm'+$80 ; O + DEFM "FN without DE" + DEFB 'F'+$80 ; P + DEFM "Parameter erro" + DEFB 'r'+$80 ; Q + DEFM "Tape loading erro" + DEFB 'r'+$80 ; R +;; comma-sp +L1537: DEFB ',',' '+$80 ; used in report line. +;; copyright +L1539: DEFB $7F ; copyright + DEFM " 1982 Sinclair Research Lt" + DEFB 'd'+$80 + + +; ------------- +; REPORT-G +; ------------- +; Note ERR_SP points here during line entry which allows the +; normal 'Out of Memory' report to be augmented to the more +; precise 'No Room for line' report. + +;; REPORT-G +; No Room for line +L1555: LD A,$10 ; i.e. 'G' -$30 -$07 + LD BC,$0000 ; this seems unnecessary. + JP L1313 ; jump back to MAIN-G + +; ----------------------------- +; Handle addition of BASIC line +; ----------------------------- +; Note this is not a subroutine but a branch of the main execution loop. +; System variable ERR_SP still points to editing error handler. +; A new line is added to the BASIC program at the appropriate place. +; An existing line with same number is deleted first. +; Entering an existing line number deletes that line. +; Entering a non-existent line allows the subsequent line to be edited next. + +;; MAIN-ADD +L155D: LD ($5C49),BC ; set E_PPC to extracted line number. + LD HL,($5C5D) ; fetch CH_ADD - points to location after the + ; initial digits (set in E_LINE_NO). + EX DE,HL ; save start of BASIC in DE. + + LD HL,L1555 ; Address: REPORT-G + PUSH HL ; is pushed on stack and addressed by ERR_SP. + ; the only error that can occur is + ; 'Out of memory'. + + LD HL,($5C61) ; fetch WORKSP - end of line. + SCF ; prepare for true subtraction. + SBC HL,DE ; find length of BASIC and + PUSH HL ; save it on stack. + LD H,B ; transfer line number + LD L,C ; to HL register. + CALL L196E ; routine LINE-ADDR will see if + ; a line with the same number exists. + JR NZ,L157D ; forward if no existing line to MAIN-ADD1. + + CALL L19B8 ; routine NEXT-ONE finds the existing line. + CALL L19E8 ; routine RECLAIM-2 reclaims it. + +;; MAIN-ADD1 +L157D: POP BC ; retrieve the length of the new line. + LD A,C ; and test if carriage return only + DEC A ; i.e. one byte long. + OR B ; result would be zero. + JR Z,L15AB ; forward to MAIN-ADD2 is so. + + PUSH BC ; save the length again. + INC BC ; adjust for inclusion + INC BC ; of line number (two bytes) + INC BC ; and line length + INC BC ; (two bytes). + DEC HL ; HL points to location before the destination + + LD DE,($5C53) ; fetch the address of PROG + PUSH DE ; and save it on the stack + CALL L1655 ; routine MAKE-ROOM creates BC spaces in + ; program area and updates pointers. + POP HL ; restore old program pointer. + LD ($5C53),HL ; and put back in PROG as it may have been + ; altered by the POINTERS routine. + + POP BC ; retrieve BASIC length + PUSH BC ; and save again. + + INC DE ; points to end of new area. + LD HL,($5C61) ; set HL to WORKSP - location after edit line. + DEC HL ; decrement to address end marker. + DEC HL ; decrement to address carriage return. + LDDR ; copy the BASIC line back to initial command. + + LD HL,($5C49) ; fetch E_PPC - line number. + EX DE,HL ; swap it to DE, HL points to last of + ; four locations. + POP BC ; retrieve length of line. + LD (HL),B ; high byte last. + DEC HL ; + LD (HL),C ; then low byte of length. + DEC HL ; + LD (HL),E ; then low byte of line number. + DEC HL ; + LD (HL),D ; then high byte range $0 - $27 (1-9999). + +;; MAIN-ADD2 +L15AB: POP AF ; drop the address of Report G + JP L12A2 ; and back to MAIN-EXEC producing a listing + ; and to reset ERR_SP in EDITOR. + + +; --------------------------- +; Initial channel information +; --------------------------- +; This initial channel information is copied from ROM to RAM, +; during initialization. It's new location is after the system +; variables and is addressed by the system variable CHANS +; which means that it can slide up and down in memory. +; The table is never searched and the last character which could be anything +; other than a comma provides a convenient resting place for DATADD. + +;; init-chan +L15AF: DEFW L09F4 ; PRINT-OUT + DEFW L10A8 ; KEY-INPUT + DEFB $4B ; 'K' + DEFW L09F4 ; PRINT-OUT + DEFW L15C4 ; REPORT-J + DEFB $53 ; 'S' + DEFW L0F81 ; ADD-CHAR + DEFW L15C4 ; REPORT-J + DEFB $52 ; 'R' + DEFW L09F4 ; PRINT-OUT + DEFW L15C4 ; REPORT-J + DEFB $50 ; 'P' + + DEFB $80 ; End Marker + +;; REPORT-J +L15C4: RST 08H ; ERROR-1 + DEFB $12 ; Error Report: Invalid I/O device + + +; ------------------- +; Initial stream data +; ------------------- +; This is the initial stream data for the seven streams $FD - $03 that is +; copied from ROM to the STRMS system variables area during initialization. +; There are reserved locations there for another 12 streams. +; Each location contains an offset to the second byte of a channel. +; The first byte of a channel can't be used as that would result in an +; offset of zero for some and zero is used to denote that a stream is closed. + +;; init-strm +L15C6: DEFB $01, $00 ; stream $FD offset to channel 'K' + DEFB $06, $00 ; stream $FE offset to channel 'S' + DEFB $0B, $00 ; stream $FF offset to channel 'R' + + DEFB $01, $00 ; stream $00 offset to channel 'K' + DEFB $01, $00 ; stream $01 offset to channel 'K' + DEFB $06, $00 ; stream $02 offset to channel 'S' + DEFB $10, $00 ; stream $03 offset to channel 'P' + +; ---------------------------- +; Control for input subroutine +; ---------------------------- +; + +;; WAIT-KEY +L15D4: BIT 5,(IY+$02) ; test TV_FLAG - clear lower screen ? + JR NZ,L15DE ; forward to WAIT-KEY1 if so. + + SET 3,(IY+$02) ; update TV_FLAG - signal reprint the edit + ; line to the lower screen. + +;; WAIT-KEY1 +L15DE: CALL L15E6 ; routine INPUT-AD is called. + RET C ; return with acceptable keys. + + JR Z,L15DE ; back to WAIT-KEY1 if no key is pressed + ; or it has been handled within INPUT-AD. + +; Note. When inputting from the keyboard all characters are returned with +; above conditions so this path is never taken. + +;; REPORT-8 +L15E4: RST 08H ; ERROR-1 + DEFB $07 ; Error Report: End of file + +; ------------------------------ +; Make HL point to input address +; ------------------------------ +; This routine fetches the address of the input stream from the current +; channel area using system variable CURCHL. + +;; INPUT-AD +L15E6: EXX ; switch in alternate set. + PUSH HL ; save HL register + LD HL,($5C51) ; fetch address of CURCHL - current channel. + INC HL ; step over output routine + INC HL ; to point to low byte of input routine. + JR L15F7 ; forward to CALL-SUB. + +; ------------------- +; Main Output Routine +; ------------------- +; The entry point OUT-CODE is called on five occasions to print +; the ASCII equivalent of a value 0-9. +; +; PRINT-A-2 is a continuation of the RST 10 to print any character. +; Both print to the current channel and the printing of control codes +; may alter that channel to divert subsequent RST 10 instructions +; to temporary routines. The normal channel is $09F4. + +;; OUT-CODE +L15EF: LD E,$30 ; add 48 decimal to give ASCII + ADD A,E ; character '0' to '9'. + +;; PRINT-A-2 +L15F2: EXX ; switch in alternate set + PUSH HL ; save HL register + LD HL,($5C51) ; fetch CURCHL the current channel. + +; input-ad rejoins here also. + +;; CALL-SUB +L15F7: LD E,(HL) ; put the low byte in E. + INC HL ; advance address. + LD D,(HL) ; put the high byte to D. + EX DE,HL ; transfer the stream to HL. + CALL L162C ; use routine CALL-JUMP. + ; in effect CALL (HL). + + POP HL ; restore saved HL register. + EXX ; switch back to the main set and + RET ; return. + +; ------------ +; Open channel +; ------------ +; This subroutine is used by the ROM to open a channel 'K', 'S', 'R' or 'P'. +; This is either for its own use or in response to a user's request, for +; example, when '#' is encountered with output - PRINT, LIST etc. +; or with input - INPUT, INKEY$ etc. +; it is entered with a system stream $FD - $FF, or a user stream $00 - $0F +; in the accumulator. + +;; CHAN-OPEN +L1601: ADD A,A ; double the stream ($FF will become $FE etc.) + ADD A,$16 ; add the offset to stream 0 from $5C00 + LD L,A ; result to L + LD H,$5C ; now form the address in STRMS area. + LD E,(HL) ; fetch low byte of CHANS offset + INC HL ; address next + LD D,(HL) ; fetch high byte of offset + LD A,D ; test that the stream is open. + OR E ; zero if closed. + JR NZ,L1610 ; forward to CHAN-OP-1 if open. + +;; REPORT-Oa +L160E: RST 08H ; ERROR-1 + DEFB $17 ; Error Report: Invalid stream + +; continue here if stream was open. Note that the offset is from CHANS +; to the second byte of the channel. + +;; CHAN-OP-1 +L1610: DEC DE ; reduce offset so it points to the channel. + LD HL,($5C4F) ; fetch CHANS the location of the base of + ; the channel information area + ADD HL,DE ; and add the offset to address the channel. + ; and continue to set flags. + +; ----------------- +; Set channel flags +; ----------------- +; This subroutine is used from ED-EDIT, str$ and read-in to reset the +; current channel when it has been temporarily altered. + +;; CHAN-FLAG +L1615: LD ($5C51),HL ; set CURCHL system variable to the + ; address in HL + RES 4,(IY+$30) ; update FLAGS2 - signal K channel not in use. + ; Note. provide a default for channel 'R'. + INC HL ; advance past + INC HL ; output routine. + INC HL ; advance past + INC HL ; input routine. + LD C,(HL) ; pick up the letter. + LD HL,L162D ; address: chn-cd-lu + CALL L16DC ; routine INDEXER finds offset to a + ; flag-setting routine. + + RET NC ; but if the letter wasn't found in the + ; table just return now. - channel 'R'. + + LD D,$00 ; prepare to add + LD E,(HL) ; offset to E + ADD HL,DE ; add offset to location of offset to form + ; address of routine + +;; CALL-JUMP +L162C: JP (HL) ; jump to the routine + +; Footnote. calling any location that holds JP (HL) is the equivalent to +; a pseudo Z80 instruction CALL (HL). The ROM uses the instruction above. + +; -------------------------- +; Channel code look-up table +; -------------------------- +; This table is used by the routine above to find one of the three +; flag setting routines below it. +; A zero end-marker is required as channel 'R' is not present. + +;; chn-cd-lu +L162D: DEFB 'K', L1634-$-1 ; offset $06 to CHAN-K + DEFB 'S', L1642-$-1 ; offset $12 to CHAN-S + DEFB 'P', L164D-$-1 ; offset $1B to CHAN-P + + DEFB $00 ; end marker. + +; -------------- +; Channel K flag +; -------------- +; routine to set flags for lower screen/keyboard channel. + +;; CHAN-K +L1634: SET 0,(IY+$02) ; update TV_FLAG - signal lower screen in use + RES 5,(IY+$01) ; update FLAGS - signal no new key + SET 4,(IY+$30) ; update FLAGS2 - signal K channel in use + JR L1646 ; forward to CHAN-S-1 for indirect exit + +; -------------- +; Channel S flag +; -------------- +; routine to set flags for upper screen channel. + +;; CHAN-S +L1642: RES 0,(IY+$02) ; TV_FLAG - signal main screen in use + +;; CHAN-S-1 +L1646: RES 1,(IY+$01) ; update FLAGS - signal printer not in use + JP L0D4D ; jump back to TEMPS and exit via that + ; routine after setting temporary attributes. +; -------------- +; Channel P flag +; -------------- +; This routine sets a flag so that subsequent print related commands +; print to printer or update the relevant system variables. +; This status remains in force until reset by the routine above. + +;; CHAN-P +L164D: SET 1,(IY+$01) ; update FLAGS - signal printer in use + RET ; return + +; ----------------------- +; Just one space required +; ----------------------- +; This routine is called once only to create a single space +; in workspace by ADD-CHAR. It is slightly quicker than using a RST $30. +; There are several instances in the calculator where the sequence +; ld bc, 1; rst $30 could be replaced by a call to this routine but it +; only gives a saving of one byte each time. + +;; ONE-SPACE +L1652: LD BC,$0001 ; create space for a single character. + +; --------- +; Make Room +; --------- +; This entry point is used to create BC spaces in various areas such as +; program area, variables area, workspace etc.. +; The entire free RAM is available to each BASIC statement. +; On entry, HL addresses where the first location is to be created. +; Afterwards, HL will point to the location before this. + +;; MAKE-ROOM +L1655: PUSH HL ; save the address pointer. + CALL L1F05 ; routine TEST-ROOM checks if room + ; exists and generates an error if not. + POP HL ; restore the address pointer. + CALL L1664 ; routine POINTERS updates the + ; dynamic memory location pointers. + ; DE now holds the old value of STKEND. + LD HL,($5C65) ; fetch new STKEND the top destination. + + EX DE,HL ; HL now addresses the top of the area to + ; be moved up - old STKEND. + LDDR ; the program, variables, etc are moved up. + RET ; return with new area ready to be populated. + ; HL points to location before new area, + ; and DE to last of new locations. + +; ----------------------------------------------- +; Adjust pointers before making or reclaiming room +; ----------------------------------------------- +; This routine is called by MAKE-ROOM to adjust upwards and by RECLAIM to +; adjust downwards the pointers within dynamic memory. +; The fourteen pointers to dynamic memory, starting with VARS and ending +; with STKEND, are updated adding BC if they are higher than the position +; in HL. +; The system variables are in no particular order except that STKEND, the first +; free location after dynamic memory must be the last encountered. + +;; POINTERS +L1664: PUSH AF ; preserve accumulator. + PUSH HL ; put pos pointer on stack. + LD HL,$5C4B ; address VARS the first of the + LD A,$0E ; fourteen variables to consider. + +;; PTR-NEXT +L166B: LD E,(HL) ; fetch the low byte of the system variable. + INC HL ; advance address. + LD D,(HL) ; fetch high byte of the system variable. + EX (SP),HL ; swap pointer on stack with the variable + ; pointer. + AND A ; prepare to subtract. + SBC HL,DE ; subtract variable address + ADD HL,DE ; and add back + EX (SP),HL ; swap pos with system variable pointer + JR NC,L167F ; forward to PTR-DONE if var before pos + + PUSH DE ; save system variable address. + EX DE,HL ; transfer to HL + ADD HL,BC ; add the offset + EX DE,HL ; back to DE + LD (HL),D ; load high byte + DEC HL ; move back + LD (HL),E ; load low byte + INC HL ; advance to high byte + POP DE ; restore old system variable address. + +;; PTR-DONE +L167F: INC HL ; address next system variable. + DEC A ; decrease counter. + JR NZ,L166B ; back to PTR-NEXT if more. + EX DE,HL ; transfer old value of STKEND to HL. + ; Note. this has always been updated. + POP DE ; pop the address of the position. + + POP AF ; pop preserved accumulator. + AND A ; clear carry flag preparing to subtract. + + SBC HL,DE ; subtract position from old stkend + LD B,H ; to give number of data bytes + LD C,L ; to be moved. + INC BC ; increment as we also copy byte at old STKEND. + ADD HL,DE ; recompute old stkend. + EX DE,HL ; transfer to DE. + RET ; return. + + + +; ------------------- +; Collect line number +; ------------------- +; This routine extracts a line number, at an address that has previously +; been found using LINE-ADDR, and it is entered at LINE-NO. If it encounters +; the program 'end-marker' then the previous line is used and if that +; should also be unacceptable then zero is used as it must be a direct +; command. The program end-marker is the variables end-marker $80, or +; if variables exist, then the first character of any variable name. + +;; LINE-ZERO +L168F: DEFB $00, $00 ; dummy line number used for direct commands + + +;; LINE-NO-A +L1691: EX DE,HL ; fetch the previous line to HL and set + LD DE,$168F ; DE to LINE-ZERO should HL also fail. + +; -> The Entry Point. + +;; LINE-NO +L1695: LD A,(HL) ; fetch the high byte - max $2F + AND $C0 ; mask off the invalid bits. + JR NZ,L1691 ; to LINE-NO-A if an end-marker. + + LD D,(HL) ; reload the high byte. + INC HL ; advance address. + LD E,(HL) ; pick up the low byte. + RET ; return from here. + +; ------------------- +; Handle reserve room +; ------------------- +; This is a continuation of the restart BC-SPACES + +;; RESERVE +L169E: LD HL,($5C63) ; STKBOT first location of calculator stack + DEC HL ; make one less than new location + CALL L1655 ; routine MAKE-ROOM creates the room. + INC HL ; address the first new location + INC HL ; advance to second + POP BC ; restore old WORKSP + LD ($5C61),BC ; system variable WORKSP was perhaps + ; changed by POINTERS routine. + POP BC ; restore count for return value. + EX DE,HL ; switch. DE = location after first new space + INC HL ; HL now location after new space + RET ; return. + +; --------------------------- +; Clear various editing areas +; --------------------------- +; This routine sets the editing area, workspace and calculator stack +; to their minimum configurations as at initialization and indeed this +; routine could have been relied on to perform that task. +; This routine uses HL only and returns with that register holding +; WORKSP/STKBOT/STKEND though no use is made of this. The routines also +; reset MEM to its usual place in the systems variable area should it +; have been relocated to a FOR-NEXT variable. The main entry point +; SET-MIN is called at the start of the MAIN-EXEC loop and prior to +; displaying an error. + +;; SET-MIN +L16B0: LD HL,($5C59) ; fetch E_LINE + LD (HL),$0D ; insert carriage return + LD ($5C5B),HL ; make K_CUR keyboard cursor point there. + INC HL ; next location + LD (HL),$80 ; holds end-marker $80 + INC HL ; next location becomes + LD ($5C61),HL ; start of WORKSP + +; This entry point is used prior to input and prior to the execution, +; or parsing, of each statement. + +;; SET-WORK +L16BF: LD HL,($5C61) ; fetch WORKSP value + LD ($5C63),HL ; and place in STKBOT + +; This entry point is used to move the stack back to its normal place +; after temporary relocation during line entry and also from ERROR-3 + +;; SET-STK +L16C5: LD HL,($5C63) ; fetch STKBOT value + LD ($5C65),HL ; and place in STKEND. + + PUSH HL ; perhaps an obsolete entry point. + LD HL,$5C92 ; normal location of MEM-0 + LD ($5C68),HL ; is restored to system variable MEM. + POP HL ; saved value not required. + RET ; return. + +; ------------------ +; Reclaim edit-line? +; ------------------ +; This seems to be legacy code from the ZX80/ZX81 as it is +; not used in this ROM. +; That task, in fact, is performed here by the dual-area routine CLEAR-SP. +; This routine is designed to deal with something that is known to be in the +; edit buffer and not workspace. +; On entry, HL must point to the end of the something to be deleted. + +;; REC-EDIT +L16D4: LD DE,($5C59) ; fetch start of edit line from E_LINE. + JP L19E5 ; jump forward to RECLAIM-1. + +; -------------------------- +; The Table INDEXING routine +; -------------------------- +; This routine is used to search two-byte hash tables for a character +; held in C, returning the address of the following offset byte. +; if it is known that the character is in the table e.g. for priorities, +; then the table requires no zero end-marker. If this is not known at the +; outset then a zero end-marker is required and carry is set to signal +; success. + +;; INDEXER-1 +L16DB: INC HL ; address the next pair of values. + +; -> The Entry Point. + +;; INDEXER +L16DC: LD A,(HL) ; fetch the first byte of pair + AND A ; is it the end-marker ? + RET Z ; return with carry reset if so. + + CP C ; is it the required character ? + INC HL ; address next location. + JR NZ,L16DB ; back to INDEXER-1 if no match. + + SCF ; else set the carry flag. + RET ; return with carry set + +; -------------------------------- +; The Channel and Streams Routines +; -------------------------------- +; A channel is an input/output route to a hardware device +; and is identified to the system by a single letter e.g. 'K' for +; the keyboard. A channel can have an input and output route +; associated with it in which case it is bi-directional like +; the keyboard. Others like the upper screen 'S' are output +; only and the input routine usually points to a report message. +; Channels 'K' and 'S' are system channels and it would be inappropriate +; to close the associated streams so a mechanism is provided to +; re-attach them. When the re-attachment is no longer required, then +; closing these streams resets them as at initialization. +; The same also would have applied to channel 'R', the RS232 channel +; as that is used by the system. It's input stream seems to have been +; removed and it is not available to the user. However the channel could +; not be removed entirely as its output routine was used by the system. +; As a result of removing this channel, channel 'P', the printer is +; erroneously treated as a system channel. +; Ironically the tape streamer is not accessed through streams and +; channels. +; Early demonstrations of the Spectrum showed a single microdrive being +; controlled by this ROM. Adverts also said that the network and RS232 +; were in this ROM. Channels 'M' and 'N' are user channels and have been +; removed successfully if, as seems vaguely possible, they existed. + +; --------------------- +; Handle CLOSE# command +; --------------------- +; This command allows streams to be closed after use. +; Any temporary memory areas used by the stream would be reclaimed and +; finally flags set or reset if necessary. + +;; CLOSE +L16E5: CALL L171E ; routine STR-DATA fetches parameter + ; from calculator stack and gets the + ; existing STRMS data pointer address in HL + ; and stream offset from CHANS in BC. + + ; Note. this offset could be zero if the + ; stream is already closed. A check for this + ; should occur now and an error should be + ; generated, for example, + ; Report S 'Stream already closed'. + + CALL L1701 ; routine CLOSE-2 would perform any actions + ; peculiar to that stream without disturbing + ; data pointer to STRMS entry in HL. + + LD BC,$0000 ; the stream is to be blanked. + LD DE,$A3E2 ; the number of bytes from stream 4, $5C1E, + ; to $10000 + EX DE,HL ; transfer offset to HL, STRMS data pointer + ; to DE. + ADD HL,DE ; add the offset to the data pointer. + JR C,L16FC ; forward to CLOSE-1 if a non-system stream. + ; i.e. higher than 3. + +; proceed with a negative result. + + LD BC,L15C6 + 14 ; prepare the address of the byte after + ; the initial stream data in ROM. ($15D4) + ADD HL,BC ; index into the data table with negative value. + LD C,(HL) ; low byte to C + INC HL ; address next. + LD B,(HL) ; high byte to B. + +; and for streams 0 - 3 just enter the initial data back into the STRMS entry +; streams 0 - 2 can't be closed as they are shared by the operating system. +; -> for streams 4 - 15 then blank the entry. + +;; CLOSE-1 +L16FC: EX DE,HL ; address of stream to HL. + LD (HL),C ; place zero (or low byte). + INC HL ; next address. + LD (HL),B ; place zero (or high byte). + RET ; return. + +; ------------------ +; CLOSE-2 Subroutine +; ------------------ +; There is not much point in coming here. +; The purpose was once to find the offset to a special closing routine, +; in this ROM and within 256 bytes of the close stream look up table that +; would reclaim any buffers associated with a stream. At least one has been +; removed. + +;; CLOSE-2 +L1701: PUSH HL ; * save address of stream data pointer + ; in STRMS on the machine stack. + LD HL,($5C4F) ; fetch CHANS address to HL + ADD HL,BC ; add the offset to address the second + ; byte of the output routine hopefully. + INC HL ; step past + INC HL ; the input routine. + INC HL ; to address channel's letter + LD C,(HL) ; pick it up in C. + ; Note. but if stream is already closed we + ; get the value $10 (the byte preceding 'K'). + EX DE,HL ; save the pointer to the letter in DE. + LD HL,L1716 ; address: cl-str-lu in ROM. + CALL L16DC ; routine INDEXER uses the code to get + ; the 8-bit offset from the current point to + ; the address of the closing routine in ROM. + ; Note. it won't find $10 there! + LD C,(HL) ; transfer the offset to C. + LD B,$00 ; prepare to add. + ADD HL,BC ; add offset to point to the address of the + ; routine that closes the stream. + ; (and presumably removes any buffers that + ; are associated with it.) + JP (HL) ; jump to that routine. + +; -------------------------- +; CLOSE stream look-up table +; -------------------------- +; This table contains an entry for a letter found in the CHANS area. +; followed by an 8-bit displacement, from that byte's address in the +; table to the routine that performs any ancillary actions associated +; with closing the stream of that channel. +; The table doesn't require a zero end-marker as the letter has been +; picked up from a channel that has an open stream. + +;; cl-str-lu +L1716: DEFB 'K', L171C-$-1 ; offset 5 to CLOSE-STR + DEFB 'S', L171C-$-1 ; offset 3 to CLOSE-STR + DEFB 'P', L171C-$-1 ; offset 1 to CLOSE-STR + + +; ------------------------ +; Close Stream Subroutines +; ------------------------ +; The close stream routines in fact have no ancillary actions to perform +; which is not surprising with regard to 'K' and 'S'. + +;; CLOSE-STR +L171C: POP HL ; * now just restore the stream data pointer + RET ; in STRMS and return. + +; ----------- +; Stream data +; ----------- +; This routine finds the data entry in the STRMS area for the specified +; stream which is passed on the calculator stack. It returns with HL +; pointing to this system variable and BC holding a displacement from +; the CHANS area to the second byte of the stream's channel. If BC holds +; zero, then that signifies that the stream is closed. + +;; STR-DATA +L171E: CALL L1E94 ; routine FIND-INT1 fetches parameter to A + CP $10 ; is it less than 16d ? + JR C,L1727 ; skip forward to STR-DATA1 if so. + +;; REPORT-Ob +L1725: RST 08H ; ERROR-1 + DEFB $17 ; Error Report: Invalid stream + +;; STR-DATA1 +L1727: ADD A,$03 ; add the offset for 3 system streams. + ; range 00 - 15d becomes 3 - 18d. + RLCA ; double as there are two bytes per + ; stream - now 06 - 36d + LD HL,$5C10 ; address STRMS - the start of the streams + ; data area in system variables. + LD C,A ; transfer the low byte to A. + LD B,$00 ; prepare to add offset. + ADD HL,BC ; add to address the data entry in STRMS. + +; the data entry itself contains an offset from CHANS to the address of the +; stream + + LD C,(HL) ; low byte of displacement to C. + INC HL ; address next. + LD B,(HL) ; high byte of displacement to B. + DEC HL ; step back to leave HL pointing to STRMS + ; data entry. + RET ; return with CHANS displacement in BC + ; and address of stream data entry in HL. + +; -------------------- +; Handle OPEN# command +; -------------------- +; Command syntax example: OPEN #5,"s" +; On entry the channel code entry is on the calculator stack with the next +; value containing the stream identifier. They have to swapped. + +;; OPEN +L1736: RST 28H ;; FP-CALC ;s,c. + DEFB $01 ;;exchange ;c,s. + DEFB $38 ;;end-calc + + CALL L171E ; routine STR-DATA fetches the stream off + ; the stack and returns with the CHANS + ; displacement in BC and HL addressing + ; the STRMS data entry. + LD A,B ; test for zero which + OR C ; indicates the stream is closed. + JR Z,L1756 ; skip forward to OPEN-1 if so. + +; if it is a system channel then it can re-attached. + + EX DE,HL ; save STRMS address in DE. + LD HL,($5C4F) ; fetch CHANS. + ADD HL,BC ; add the offset to address the second + ; byte of the channel. + INC HL ; skip over the + INC HL ; input routine. + INC HL ; and address the letter. + LD A,(HL) ; pick up the letter. + EX DE,HL ; save letter pointer and bring back + ; the STRMS pointer. + + CP $4B ; is it 'K' ? + JR Z,L1756 ; forward to OPEN-1 if so + + CP $53 ; is it 'S' ? + JR Z,L1756 ; forward to OPEN-1 if so + + CP $50 ; is it 'P' ? + JR NZ,L1725 ; back to REPORT-Ob if not. + ; to report 'Invalid stream'. + +; continue if one of the upper-case letters was found. +; and rejoin here from above if stream was closed. + +;; OPEN-1 +L1756: CALL L175D ; routine OPEN-2 opens the stream. + +; it now remains to update the STRMS variable. + + LD (HL),E ; insert or overwrite the low byte. + INC HL ; address high byte in STRMS. + LD (HL),D ; insert or overwrite the high byte. + RET ; return. + +; ----------------- +; OPEN-2 Subroutine +; ----------------- +; There is some point in coming here as, as well as once creating buffers, +; this routine also sets flags. + +;; OPEN-2 +L175D: PUSH HL ; * save the STRMS data entry pointer. + CALL L2BF1 ; routine STK-FETCH now fetches the + ; parameters of the channel string. + ; start in DE, length in BC. + + LD A,B ; test that it is not + OR C ; the null string. + JR NZ,L1767 ; skip forward to OPEN-3 with 1 character + ; or more! + +;; REPORT-Fb +L1765: RST 08H ; ERROR-1 + DEFB $0E ; Error Report: Invalid file name + +;; OPEN-3 +L1767: PUSH BC ; save the length of the string. + LD A,(DE) ; pick up the first character. + ; Note. if the second character is used to + ; distinguish between a binary or text + ; channel then it will be simply a matter + ; of setting bit 7 of FLAGX. + AND $DF ; make it upper-case. + LD C,A ; place it in C. + LD HL,L177A ; address: op-str-lu is loaded. + CALL L16DC ; routine INDEXER will search for letter. + JR NC,L1765 ; back to REPORT-F if not found + ; 'Invalid filename' + + LD C,(HL) ; fetch the displacement to opening routine. + LD B,$00 ; prepare to add. + ADD HL,BC ; now form address of opening routine. + POP BC ; restore the length of string. + JP (HL) ; now jump forward to the relevant routine. + +; ------------------------- +; OPEN stream look-up table +; ------------------------- +; The open stream look-up table consists of matched pairs. +; The channel letter is followed by an 8-bit displacement to the +; associated stream-opening routine in this ROM. +; The table requires a zero end-marker as the letter has been +; provided by the user and not the operating system. + +;; op-str-lu +L177A: DEFB 'K', L1781-$-1 ; $06 offset to OPEN-K + DEFB 'S', L1785-$-1 ; $08 offset to OPEN-S + DEFB 'P', L1789-$-1 ; $0A offset to OPEN-P + + DEFB $00 ; end-marker. + +; ---------------------------- +; The Stream Opening Routines. +; ---------------------------- +; These routines would have opened any buffers associated with the stream +; before jumping forward to to OPEN-END with the displacement value in E +; and perhaps a modified value in BC. The strange pathing does seem to +; provide for flexibility in this respect. +; +; There is no need to open the printer buffer as it is there already +; even if you are still saving up for a ZX Printer or have moved onto +; something bigger. In any case it would have to be created after +; the system variables but apart from that it is a simple task +; and all but one of the ROM routines can handle a buffer in that position. +; (PR-ALL-6 would require an extra 3 bytes of code). +; However it wouldn't be wise to have two streams attached to the ZX Printer +; as you can now, so one assumes that if PR_CC_hi was non-zero then +; the OPEN-P routine would have refused to attach a stream if another +; stream was attached. + +; Something of significance is being passed to these ghost routines in the +; second character. Strings 'RB', 'RT' perhaps or a drive/station number. +; The routine would have to deal with that and exit to OPEN_END with BC +; containing $0001 or more likely there would be an exit within the routine. +; Anyway doesn't matter, these routines are long gone. + +; ----------------- +; OPEN-K Subroutine +; ----------------- +; Open Keyboard stream. + +;; OPEN-K +L1781: LD E,$01 ; 01 is offset to second byte of channel 'K'. + JR L178B ; forward to OPEN-END + +; ----------------- +; OPEN-S Subroutine +; ----------------- +; Open Screen stream. + +;; OPEN-S +L1785: LD E,$06 ; 06 is offset to 2nd byte of channel 'S' + JR L178B ; to OPEN-END + +; ----------------- +; OPEN-P Subroutine +; ----------------- +; Open Printer stream. + +;; OPEN-P +L1789: LD E,$10 ; 16d is offset to 2nd byte of channel 'P' + +;; OPEN-END +L178B: DEC BC ; the stored length of 'K','S','P' or + ; whatever is now tested. ?? + LD A,B ; test now if initial or residual length + OR C ; is one character. + JR NZ,L1765 ; to REPORT-Fb 'Invalid file name' if not. + + LD D,A ; load D with zero to form the displacement + ; in the DE register. + POP HL ; * restore the saved STRMS pointer. + RET ; return to update STRMS entry thereby + ; signaling stream is open. + +; ---------------------------------------- +; Handle CAT, ERASE, FORMAT, MOVE commands +; ---------------------------------------- +; These just generate an error report as the ROM is 'incomplete'. +; +; Luckily this provides a mechanism for extending these in a shadow ROM +; but without the powerful mechanisms set up in this ROM. +; An instruction fetch on $0008 may page in a peripheral ROM, +; e.g. the Sinclair Interface 1 ROM, to handle these commands. +; However that wasn't the plan. +; Development of this ROM continued for another three months until the cost +; of replacing it and the manual became unfeasible. +; The ultimate power of channels and streams died at birth. + +;; CAT-ETC +L1793: JR L1725 ; to REPORT-Ob + +; ----------------- +; Perform AUTO-LIST +; ----------------- +; This produces an automatic listing in the upper screen. + +;; AUTO-LIST +L1795: LD ($5C3F),SP ; save stack pointer in LIST_SP + LD (IY+$02),$10 ; update TV_FLAG set bit 3 + CALL L0DAF ; routine CL-ALL. + SET 0,(IY+$02) ; update TV_FLAG - signal lower screen in use + + LD B,(IY+$31) ; fetch DF_SZ to B. + CALL L0E44 ; routine CL-LINE clears lower display + ; preserving B. + RES 0,(IY+$02) ; update TV_FLAG - signal main screen in use + SET 0,(IY+$30) ; update FLAGS2 - signal unnecessary to + ; clear main screen. + LD HL,($5C49) ; fetch E_PPC current edit line to HL. + LD DE,($5C6C) ; fetch S_TOP to DE, the current top line + ; (initially zero) + AND A ; prepare for true subtraction. + SBC HL,DE ; subtract and + ADD HL,DE ; add back. + JR C,L17E1 ; to AUTO-L-2 if S_TOP higher than E_PPC + ; to set S_TOP to E_PPC + + PUSH DE ; save the top line number. + CALL L196E ; routine LINE-ADDR gets address of E_PPC. + LD DE,$02C0 ; prepare known number of characters in + ; the default upper screen. + EX DE,HL ; offset to HL, program address to DE. + SBC HL,DE ; subtract high value from low to obtain + ; negated result used in addition. + EX (SP),HL ; swap result with top line number on stack. + CALL L196E ; routine LINE-ADDR gets address of that + ; top line in HL and next line in DE. + POP BC ; restore the result to balance stack. + +;; AUTO-L-1 +L17CE: PUSH BC ; save the result. + CALL L19B8 ; routine NEXT-ONE gets address in HL of + ; line after auto-line (in DE). + POP BC ; restore result. + ADD HL,BC ; compute back. + JR C,L17E4 ; to AUTO-L-3 if line 'should' appear + + EX DE,HL ; address of next line to HL. + LD D,(HL) ; get line + INC HL ; number + LD E,(HL) ; in DE. + DEC HL ; adjust back to start. + LD ($5C6C),DE ; update S_TOP. + JR L17CE ; to AUTO-L-1 until estimate reached. + +; --- + +; the jump was to here if S_TOP was greater than E_PPC + +;; AUTO-L-2 +L17E1: LD ($5C6C),HL ; make S_TOP the same as E_PPC. + +; continue here with valid starting point from above or good estimate +; from computation + +;; AUTO-L-3 +L17E4: LD HL,($5C6C) ; fetch S_TOP line number to HL. + CALL L196E ; routine LINE-ADDR gets address in HL. + ; address of next in DE. + JR Z,L17ED ; to AUTO-L-4 if line exists. + + EX DE,HL ; else use address of next line. + +;; AUTO-L-4 +L17ED: CALL L1833 ; routine LIST-ALL >>> + +; The return will be to here if no scrolling occurred + + RES 4,(IY+$02) ; update TV_FLAG - signal no auto listing. + RET ; return. + +; ------------ +; Handle LLIST +; ------------ +; A short form of LIST #3. The listing goes to stream 3 - default printer. + +;; LLIST +L17F5: LD A,$03 ; the usual stream for ZX Printer + JR L17FB ; forward to LIST-1 + +; ----------- +; Handle LIST +; ----------- +; List to any stream. +; Note. While a starting line can be specified it is +; not possible to specify an end line. +; Just listing a line makes it the current edit line. + +;; LIST +L17F9: LD A,$02 ; default is stream 2 - the upper screen. + +;; LIST-1 +L17FB: LD (IY+$02),$00 ; the TV_FLAG is initialized with bit 0 reset + ; indicating upper screen in use. + CALL L2530 ; routine SYNTAX-Z - checking syntax ? + CALL NZ,L1601 ; routine CHAN-OPEN if in run-time. + + RST 18H ; GET-CHAR + CALL L2070 ; routine STR-ALTER will alter if '#'. + JR C,L181F ; forward to LIST-4 not a '#' . + + + RST 18H ; GET-CHAR + CP $3B ; is it ';' ? + JR Z,L1814 ; skip to LIST-2 if so. + + CP $2C ; is it ',' ? + JR NZ,L181A ; forward to LIST-3 if neither separator. + +; we have, say, LIST #15, and a number must follow the separator. + +;; LIST-2 +L1814: RST 20H ; NEXT-CHAR + CALL L1C82 ; routine EXPT-1NUM + JR L1822 ; forward to LIST-5 + +; --- + +; the branch was here with just LIST #3 etc. + +;; LIST-3 +L181A: CALL L1CE6 ; routine USE-ZERO + JR L1822 ; forward to LIST-5 + +; --- + +; the branch was here with LIST + +;; LIST-4 +L181F: CALL L1CDE ; routine FETCH-NUM checks if a number + ; follows else uses zero. + +;; LIST-5 +L1822: CALL L1BEE ; routine CHECK-END quits if syntax OK >>> + + CALL L1E99 ; routine FIND-INT2 fetches the number + ; from the calculator stack in run-time. + LD A,B ; fetch high byte of line number and + AND $3F ; make less than $40 so that NEXT-ONE + ; (from LINE-ADDR) doesn't lose context. + ; Note. this is not satisfactory and the typo + ; LIST 20000 will list an entirely different + ; section than LIST 2000. Such typos are not + ; available for checking if they are direct + ; commands. + + LD H,A ; transfer the modified + LD L,C ; line number to HL. + LD ($5C49),HL ; update E_PPC to new line number. + CALL L196E ; routine LINE-ADDR gets the address of the + ; line. + +; This routine is called from AUTO-LIST + +;; LIST-ALL +L1833: LD E,$01 ; signal current line not yet printed + +;; LIST-ALL-2 +L1835: CALL L1855 ; routine OUT-LINE outputs a BASIC line + ; using PRINT-OUT and makes an early return + ; when no more lines to print. >>> + + RST 10H ; PRINT-A prints the carriage return (in A) + + BIT 4,(IY+$02) ; test TV_FLAG - automatic listing ? + JR Z,L1835 ; back to LIST-ALL-2 if not + ; (loop exit is via OUT-LINE) + +; continue here if an automatic listing required. + + LD A,($5C6B) ; fetch DF_SZ lower display file size. + SUB (IY+$4F) ; subtract S_POSN_hi ithe current line number. + JR NZ,L1835 ; back to LIST-ALL-2 if upper screen not full. + + XOR E ; A contains zero, E contains one if the + ; current edit line has not been printed + ; or zero if it has (from OUT-LINE). + RET Z ; return if the screen is full and the line + ; has been printed. + +; continue with automatic listings if the screen is full and the current +; edit line is missing. OUT-LINE will scroll automatically. + + PUSH HL ; save the pointer address. + PUSH DE ; save the E flag. + LD HL,$5C6C ; fetch S_TOP the rough estimate. + CALL L190F ; routine LN-FETCH updates S_TOP with + ; the number of the next line. + POP DE ; restore the E flag. + POP HL ; restore the address of the next line. + JR L1835 ; back to LIST-ALL-2. + +; ------------------------ +; Print a whole BASIC line +; ------------------------ +; This routine prints a whole BASIC line and it is called +; from LIST-ALL to output the line to current channel +; and from ED-EDIT to 'sprint' the line to the edit buffer. + +;; OUT-LINE +L1855: LD BC,($5C49) ; fetch E_PPC the current line which may be + ; unchecked and not exist. + CALL L1980 ; routine CP-LINES finds match or line after. + LD D,$3E ; prepare cursor '>' in D. + JR Z,L1865 ; to OUT-LINE1 if matched or line after. + + LD DE,$0000 ; put zero in D, to suppress line cursor. + RL E ; pick up carry in E if line before current + ; leave E zero if same or after. + +;; OUT-LINE1 +L1865: LD (IY+$2D),E ; save flag in BREG which is spare. + LD A,(HL) ; get high byte of line number. + CP $40 ; is it too high ($2F is maximum possible) ? + POP BC ; drop the return address and + RET NC ; make an early return if so >>> + + PUSH BC ; save return address + CALL L1A28 ; routine OUT-NUM-2 to print addressed number + ; with leading space. + INC HL ; skip low number byte. + INC HL ; and the two + INC HL ; length bytes. + RES 0,(IY+$01) ; update FLAGS - signal leading space required. + LD A,D ; fetch the cursor. + AND A ; test for zero. + JR Z,L1881 ; to OUT-LINE3 if zero. + + + RST 10H ; PRINT-A prints '>' the current line cursor. + +; this entry point is called from ED-COPY + +;; OUT-LINE2 +L187D: SET 0,(IY+$01) ; update FLAGS - suppress leading space. + +;; OUT-LINE3 +L1881: PUSH DE ; save flag E for a return value. + EX DE,HL ; save HL address in DE. + RES 2,(IY+$30) ; update FLAGS2 - signal NOT in QUOTES. + + LD HL,$5C3B ; point to FLAGS. + RES 2,(HL) ; signal 'K' mode. (starts before keyword) + BIT 5,(IY+$37) ; test FLAGX - input mode ? + JR Z,L1894 ; forward to OUT-LINE4 if not. + + SET 2,(HL) ; signal 'L' mode. (used for input) + +;; OUT-LINE4 +L1894: LD HL,($5C5F) ; fetch X_PTR - possibly the error pointer + ; address. + AND A ; clear the carry flag. + SBC HL,DE ; test if an error address has been reached. + JR NZ,L18A1 ; forward to OUT-LINE5 if not. + + LD A,$3F ; load A with '?' the error marker. + CALL L18C1 ; routine OUT-FLASH to print flashing marker. + +;; OUT-LINE5 +L18A1: CALL L18E1 ; routine OUT-CURS will print the cursor if + ; this is the right position. + EX DE,HL ; restore address pointer to HL. + LD A,(HL) ; fetch the addressed character. + CALL L18B6 ; routine NUMBER skips a hidden floating + ; point number if present. + INC HL ; now increment the pointer. + CP $0D ; is character end-of-line ? + JR Z,L18B4 ; to OUT-LINE6, if so, as line is finished. + + EX DE,HL ; save the pointer in DE. + CALL L1937 ; routine OUT-CHAR to output character/token. + + JR L1894 ; back to OUT-LINE4 until entire line is done. + +; --- + +;; OUT-LINE6 +L18B4: POP DE ; bring back the flag E, zero if current + ; line printed else 1 if still to print. + RET ; return with A holding $0D + +; ------------------------- +; Check for a number marker +; ------------------------- +; this subroutine is called from two processes. while outputting BASIC lines +; and while searching statements within a BASIC line. +; during both, this routine will pass over an invisible number indicator +; and the five bytes floating-point number that follows it. +; Note that this causes floating point numbers to be stripped from +; the BASIC line when it is fetched to the edit buffer by OUT_LINE. +; the number marker also appears after the arguments of a DEF FN statement +; and may mask old 5-byte string parameters. + +;; NUMBER +L18B6: CP $0E ; character fourteen ? + RET NZ ; return if not. + + INC HL ; skip the character + INC HL ; and five bytes + INC HL ; following. + INC HL ; + INC HL ; + INC HL ; + LD A,(HL) ; fetch the following character + RET ; for return value. + +; -------------------------- +; Print a flashing character +; -------------------------- +; This subroutine is called from OUT-LINE to print a flashing error +; marker '?' or from the next routine to print a flashing cursor e.g. 'L'. +; However, this only gets called from OUT-LINE when printing the edit line +; or the input buffer to the lower screen so a direct call to $09F4 can +; be used, even though out-line outputs to other streams. +; In fact the alternate set is used for the whole routine. + +;; OUT-FLASH +L18C1: EXX ; switch in alternate set + + LD HL,($5C8F) ; fetch L = ATTR_T, H = MASK-T + PUSH HL ; save masks. + RES 7,H ; reset flash mask bit so active. + SET 7,L ; make attribute FLASH. + LD ($5C8F),HL ; resave ATTR_T and MASK-T + + LD HL,$5C91 ; address P_FLAG + LD D,(HL) ; fetch to D + PUSH DE ; and save. + LD (HL),$00 ; clear inverse, over, ink/paper 9 + + CALL L09F4 ; routine PRINT-OUT outputs character + ; without the need to vector via RST 10. + + POP HL ; pop P_FLAG to H. + LD (IY+$57),H ; and restore system variable P_FLAG. + POP HL ; restore temporary masks + LD ($5C8F),HL ; and restore system variables ATTR_T/MASK_T + + EXX ; switch back to main set + RET ; return + +; ---------------- +; Print the cursor +; ---------------- +; This routine is called before any character is output while outputting +; a BASIC line or the input buffer. This includes listing to a printer +; or screen, copying a BASIC line to the edit buffer and printing the +; input buffer or edit buffer to the lower screen. It is only in the +; latter two cases that it has any relevance and in the last case it +; performs another very important function also. + +;; OUT-CURS +L18E1: LD HL,($5C5B) ; fetch K_CUR the current cursor address + AND A ; prepare for true subtraction. + SBC HL,DE ; test against pointer address in DE and + RET NZ ; return if not at exact position. + +; the value of MODE, maintained by KEY-INPUT, is tested and if non-zero +; then this value 'E' or 'G' will take precedence. + + LD A,($5C41) ; fetch MODE 0='KLC', 1='E', 2='G'. + RLC A ; double the value and set flags. + JR Z,L18F3 ; to OUT-C-1 if still zero ('KLC'). + + ADD A,$43 ; add 'C' - will become 'E' if originally 1 + ; or 'G' if originally 2. + JR L1909 ; forward to OUT-C-2 to print. + +; --- + +; If mode was zero then, while printing a BASIC line, bit 2 of flags has been +; set if 'THEN' or ':' was encountered as a main character and reset otherwise. +; This is now used to determine if the 'K' cursor is to be printed but this +; transient state is also now transferred permanently to bit 3 of FLAGS +; to let the interrupt routine know how to decode the next key. + +;; OUT-C-1 +L18F3: LD HL,$5C3B ; Address FLAGS + RES 3,(HL) ; signal 'K' mode initially. + LD A,$4B ; prepare letter 'K'. + BIT 2,(HL) ; test FLAGS - was the + ; previous main character ':' or 'THEN' ? + JR Z,L1909 ; forward to OUT-C-2 if so to print. + + SET 3,(HL) ; signal 'L' mode to interrupt routine. + ; Note. transient bit has been made permanent. + INC A ; augment from 'K' to 'L'. + + BIT 3,(IY+$30) ; test FLAGS2 - consider caps lock ? + ; which is maintained by KEY-INPUT. + JR Z,L1909 ; forward to OUT-C-2 if not set to print. + + LD A,$43 ; alter 'L' to 'C'. + +;; OUT-C-2 +L1909: PUSH DE ; save address pointer but OK as OUT-FLASH + ; uses alternate set without RST 10. + + CALL L18C1 ; routine OUT-FLASH to print. + + POP DE ; restore and + RET ; return. + +; ---------------------------- +; Get line number of next line +; ---------------------------- +; These two subroutines are called while editing. +; This entry point is from ED-DOWN with HL addressing E_PPC +; to fetch the next line number. +; Also from AUTO-LIST with HL addressing S_TOP just to update S_TOP +; with the value of the next line number. It gets fetched but is discarded. +; These routines never get called while the editor is being used for input. + +;; LN-FETCH +L190F: LD E,(HL) ; fetch low byte + INC HL ; address next + LD D,(HL) ; fetch high byte. + PUSH HL ; save system variable hi pointer. + EX DE,HL ; line number to HL, + INC HL ; increment as a starting point. + CALL L196E ; routine LINE-ADDR gets address in HL. + CALL L1695 ; routine LINE-NO gets line number in DE. + POP HL ; restore system variable hi pointer. + +; This entry point is from the ED-UP with HL addressing E_PPC_hi + +;; LN-STORE +L191C: BIT 5,(IY+$37) ; test FLAGX - input mode ? + RET NZ ; return if so. + ; Note. above already checked by ED-UP/ED-DOWN. + + LD (HL),D ; save high byte of line number. + DEC HL ; address lower + LD (HL),E ; save low byte of line number. + RET ; return. + +; ----------------------------------------- +; Outputting numbers at start of BASIC line +; ----------------------------------------- +; This routine entered at OUT-SP-NO is used to compute then output the first +; three digits of a 4-digit BASIC line printing a space if necessary. +; The line number, or residual part, is held in HL and the BC register +; holds a subtraction value -1000, -100 or -10. +; Note. for example line number 200 - +; space(out_char), 2(out_code), 0(out_char) final number always out-code. + +;; OUT-SP-2 +L1925: LD A,E ; will be space if OUT-CODE not yet called. + ; or $FF if spaces are suppressed. + ; else $30 ('0'). + ; (from the first instruction at OUT-CODE) + ; this guy is just too clever. + AND A ; test bit 7 of A. + RET M ; return if $FF, as leading spaces not + ; required. This is set when printing line + ; number and statement in MAIN-5. + + JR L1937 ; forward to exit via OUT-CHAR. + +; --- + +; -> the single entry point. + +;; OUT-SP-NO +L192A: XOR A ; initialize digit to 0 + +;; OUT-SP-1 +L192B: ADD HL,BC ; add negative number to HL. + INC A ; increment digit + JR C,L192B ; back to OUT-SP-1 until no carry from + ; the addition. + + SBC HL,BC ; cancel the last addition + DEC A ; and decrement the digit. + JR Z,L1925 ; back to OUT-SP-2 if it is zero. + + JP L15EF ; jump back to exit via OUT-CODE. -> + + +; ------------------------------------- +; Outputting characters in a BASIC line +; ------------------------------------- +; This subroutine ... + +;; OUT-CHAR +L1937: CALL L2D1B ; routine NUMERIC tests if it is a digit ? + JR NC,L196C ; to OUT-CH-3 to print digit without + ; changing mode. Will be 'K' mode if digits + ; are at beginning of edit line. + + CP $21 ; less than quote character ? + JR C,L196C ; to OUT-CH-3 to output controls and space. + + RES 2,(IY+$01) ; initialize FLAGS to 'K' mode and leave + ; unchanged if this character would precede + ; a keyword. + + CP $CB ; is character 'THEN' token ? + JR Z,L196C ; to OUT-CH-3 to output if so. + + CP $3A ; is it ':' ? + JR NZ,L195A ; to OUT-CH-1 if not statement separator + ; to change mode back to 'L'. + + BIT 5,(IY+$37) ; FLAGX - Input Mode ?? + JR NZ,L1968 ; to OUT-CH-2 if in input as no statements. + ; Note. this check should seemingly be at + ; the start. Commands seem inappropriate in + ; INPUT mode and are rejected by the syntax + ; checker anyway. + ; unless INPUT LINE is being used. + + BIT 2,(IY+$30) ; test FLAGS2 - is the ':' within quotes ? + JR Z,L196C ; to OUT-CH-3 if ':' is outside quoted text. + + JR L1968 ; to OUT-CH-2 as ':' is within quotes + +; --- + +;; OUT-CH-1 +L195A: CP $22 ; is it quote character '"' ? + JR NZ,L1968 ; to OUT-CH-2 with others to set 'L' mode. + + PUSH AF ; save character. + LD A,($5C6A) ; fetch FLAGS2. + XOR $04 ; toggle the quotes flag. + LD ($5C6A),A ; update FLAGS2 + POP AF ; and restore character. + +;; OUT-CH-2 +L1968: SET 2,(IY+$01) ; update FLAGS - signal L mode if the cursor + ; is next. + +;; OUT-CH-3 +L196C: RST 10H ; PRINT-A vectors the character to + ; channel 'S', 'K', 'R' or 'P'. + RET ; return. + +; ------------------------------------------- +; Get starting address of line, or line after +; ------------------------------------------- +; This routine is used often to get the address, in HL, of a BASIC line +; number supplied in HL, or failing that the address of the following line +; and the address of the previous line in DE. + +;; LINE-ADDR +L196E: PUSH HL ; save line number in HL register + LD HL,($5C53) ; fetch start of program from PROG + LD D,H ; transfer address to + LD E,L ; the DE register pair. + +;; LINE-AD-1 +L1974: POP BC ; restore the line number to BC + CALL L1980 ; routine CP-LINES compares with that + ; addressed by HL + RET NC ; return if line has been passed or matched. + ; if NZ, address of previous is in DE + + PUSH BC ; save the current line number + CALL L19B8 ; routine NEXT-ONE finds address of next + ; line number in DE, previous in HL. + EX DE,HL ; switch so next in HL + JR L1974 ; back to LINE-AD-1 for another comparison + +; -------------------- +; Compare line numbers +; -------------------- +; This routine compares a line number supplied in BC with an addressed +; line number pointed to by HL. + +;; CP-LINES +L1980: LD A,(HL) ; Load the high byte of line number and + CP B ; compare with that of supplied line number. + RET NZ ; return if yet to match (carry will be set). + + INC HL ; address low byte of + LD A,(HL) ; number and pick up in A. + DEC HL ; step back to first position. + CP C ; now compare. + RET ; zero set if exact match. + ; carry set if yet to match. + ; no carry indicates a match or + ; next available BASIC line or + ; program end marker. + +; ------------------- +; Find each statement +; ------------------- +; The single entry point EACH-STMT is used to +; 1) To find the D'th statement in a line. +; 2) To find a token in held E. + +;; not-used +L1988: INC HL ; + INC HL ; + INC HL ; + +; -> entry point. + +;; EACH-STMT +L198B: LD ($5C5D),HL ; save HL in CH_ADD + LD C,$00 ; initialize quotes flag + +;; EACH-S-1 +L1990: DEC D ; decrease statement count + RET Z ; return if zero + + + RST 20H ; NEXT-CHAR + CP E ; is it the search token ? + JR NZ,L199A ; forward to EACH-S-3 if not + + AND A ; clear carry + RET ; return signalling success. + +; --- + +;; EACH-S-2 +L1998: INC HL ; next address + LD A,(HL) ; next character + +;; EACH-S-3 +L199A: CALL L18B6 ; routine NUMBER skips if number marker + LD ($5C5D),HL ; save in CH_ADD + CP $22 ; is it quotes '"' ? + JR NZ,L19A5 ; to EACH-S-4 if not + + DEC C ; toggle bit 0 of C + +;; EACH-S-4 +L19A5: CP $3A ; is it ':' + JR Z,L19AD ; to EACH-S-5 + + CP $CB ; 'THEN' + JR NZ,L19B1 ; to EACH-S-6 + +;; EACH-S-5 +L19AD: BIT 0,C ; is it in quotes + JR Z,L1990 ; to EACH-S-1 if not + +;; EACH-S-6 +L19B1: CP $0D ; end of line ? + JR NZ,L1998 ; to EACH-S-2 + + DEC D ; decrease the statement counter + ; which should be zero else + ; 'Statement Lost'. + SCF ; set carry flag - not found + RET ; return + +; ----------------------------------------------------------------------- +; Storage of variables. For full details - see chapter 24. +; ZX Spectrum BASIC Programming by Steven Vickers 1982. +; It is bits 7-5 of the first character of a variable that allow +; the six types to be distinguished. Bits 4-0 are the reduced letter. +; So any variable name is higher that $3F and can be distinguished +; also from the variables area end-marker $80. +; +; 76543210 meaning brief outline of format. +; -------- ------------------------ ----------------------- +; 010 string variable. 2 byte length + contents. +; 110 string array. 2 byte length + contents. +; 100 array of numbers. 2 byte length + contents. +; 011 simple numeric variable. 5 bytes. +; 101 variable length named numeric. 5 bytes. +; 111 for-next loop variable. 18 bytes. +; 10000000 the variables area end-marker. +; +; Note. any of the above seven will serve as a program end-marker. +; +; ----------------------------------------------------------------------- + +; ------------ +; Get next one +; ------------ +; This versatile routine is used to find the address of the next line +; in the program area or the next variable in the variables area. +; The reason one routine is made to handle two apparently unrelated tasks +; is that it can be called indiscriminately when merging a line or a +; variable. + +;; NEXT-ONE +L19B8: PUSH HL ; save the pointer address. + LD A,(HL) ; get first byte. + CP $40 ; compare with upper limit for line numbers. + JR C,L19D5 ; forward to NEXT-O-3 if within BASIC area. + +; the continuation here is for the next variable unless the supplied +; line number was erroneously over 16383. see RESTORE command. + + BIT 5,A ; is it a string or an array variable ? + JR Z,L19D6 ; forward to NEXT-O-4 to compute length. + + ADD A,A ; test bit 6 for single-character variables. + JP M,L19C7 ; forward to NEXT-O-1 if so + + CCF ; clear the carry for long-named variables. + ; it remains set for for-next loop variables. + +;; NEXT-O-1 +L19C7: LD BC,$0005 ; set BC to 5 for floating point number + JR NC,L19CE ; forward to NEXT-O-2 if not a for/next + ; variable. + + LD C,$12 ; set BC to eighteen locations. + ; value, limit, step, line and statement. + +; now deal with long-named variables + +;; NEXT-O-2 +L19CE: RLA ; test if character inverted. carry will also + ; be set for single character variables + INC HL ; address next location. + LD A,(HL) ; and load character. + JR NC,L19CE ; back to NEXT-O-2 if not inverted bit. + ; forward immediately with single character + ; variable names. + + JR L19DB ; forward to NEXT-O-5 to add length of + ; floating point number(s etc.). + +; --- + +; this branch is for line numbers. + +;; NEXT-O-3 +L19D5: INC HL ; increment pointer to low byte of line no. + +; strings and arrays rejoin here + +;; NEXT-O-4 +L19D6: INC HL ; increment to address the length low byte. + LD C,(HL) ; transfer to C and + INC HL ; point to high byte of length. + LD B,(HL) ; transfer that to B + INC HL ; point to start of BASIC/variable contents. + +; the three types of numeric variables rejoin here + +;; NEXT-O-5 +L19DB: ADD HL,BC ; add the length to give address of next + ; line/variable in HL. + POP DE ; restore previous address to DE. + +; ------------------ +; Difference routine +; ------------------ +; This routine terminates the above routine and is also called from the +; start of the next routine to calculate the length to reclaim. + +;; DIFFER +L19DD: AND A ; prepare for true subtraction. + SBC HL,DE ; subtract the two pointers. + LD B,H ; transfer result + LD C,L ; to BC register pair. + ADD HL,DE ; add back + EX DE,HL ; and switch pointers + RET ; return values are the length of area in BC, + ; low pointer (previous) in HL, + ; high pointer (next) in DE. + +; ----------------------- +; Handle reclaiming space +; ----------------------- +; + +;; RECLAIM-1 +L19E5: CALL L19DD ; routine DIFFER immediately above + +;; RECLAIM-2 +L19E8: PUSH BC ; + + LD A,B ; + CPL ; + LD B,A ; + LD A,C ; + CPL ; + LD C,A ; + INC BC ; + + CALL L1664 ; routine POINTERS + EX DE,HL ; + POP HL ; + + ADD HL,DE ; + PUSH DE ; + LDIR ; copy bytes + + POP HL ; + RET ; + +; ---------------------------------------- +; Read line number of line in editing area +; ---------------------------------------- +; This routine reads a line number in the editing area returning the number +; in the BC register or zero if no digits exist before commands. +; It is called from LINE-SCAN to check the syntax of the digits. +; It is called from MAIN-3 to extract the line number in preparation for +; inclusion of the line in the BASIC program area. +; +; Interestingly the calculator stack is moved from its normal place at the +; end of dynamic memory to an adequate area within the system variables area. +; This ensures that in a low memory situation, that valid line numbers can +; be extracted without raising an error and that memory can be reclaimed +; by deleting lines. If the stack was in its normal place then a situation +; arises whereby the Spectrum becomes locked with no means of reclaiming space. + +;; E-LINE-NO +L19FB: LD HL,($5C59) ; load HL from system variable E_LINE. + + DEC HL ; decrease so that NEXT_CHAR can be used + ; without skipping the first digit. + + LD ($5C5D),HL ; store in the system variable CH_ADD. + + RST 20H ; NEXT-CHAR skips any noise and white-space + ; to point exactly at the first digit. + + LD HL,$5C92 ; use MEM-0 as a temporary calculator stack + ; an overhead of three locations are needed. + LD ($5C65),HL ; set new STKEND. + + CALL L2D3B ; routine INT-TO-FP will read digits till + ; a non-digit found. + CALL L2DA2 ; routine FP-TO-BC will retrieve number + ; from stack at membot. + JR C,L1A15 ; forward to E-L-1 if overflow i.e. > 65535. + ; 'Nonsense in BASIC' + + LD HL,$D8F0 ; load HL with value -9999 + ADD HL,BC ; add to line number in BC + +;; E-L-1 +L1A15: JP C,L1C8A ; to REPORT-C 'Nonsense in BASIC' if over. + ; Note. As ERR_SP points to ED_ERROR + ; the report is never produced although + ; the RST $08 will update X_PTR leading to + ; the error marker being displayed when + ; the ED_LOOP is reiterated. + ; in fact, since it is immediately + ; cancelled, any report will do. + +; a line in the range 0 - 9999 has been entered. + + JP L16C5 ; jump back to SET-STK to set the calculator + ; stack back to its normal place and exit + ; from there. + +; --------------------------------- +; Report and line number outputting +; --------------------------------- +; Entry point OUT-NUM-1 is used by the Error Reporting code to print +; the line number and later the statement number held in BC. +; If the statement was part of a direct command then -2 is used as a +; dummy line number so that zero will be printed in the report. +; This routine is also used to print the exponent of E-format numbers. +; +; Entry point OUT-NUM-2 is used from OUT-LINE to output the line number +; addressed by HL with leading spaces if necessary. + +;; OUT-NUM-1 +L1A1B: PUSH DE ; save the + PUSH HL ; registers. + XOR A ; set A to zero. + BIT 7,B ; is the line number minus two ? + JR NZ,L1A42 ; forward to OUT-NUM-4 if so to print zero + ; for a direct command. + + LD H,B ; transfer the + LD L,C ; number to HL. + LD E,$FF ; signal 'no leading zeros'. + JR L1A30 ; forward to continue at OUT-NUM-3 + +; --- + +; from OUT-LINE - HL addresses line number. + +;; OUT-NUM-2 +L1A28: PUSH DE ; save flags + LD D,(HL) ; high byte to D + INC HL ; address next + LD E,(HL) ; low byte to E + PUSH HL ; save pointer + EX DE,HL ; transfer number to HL + LD E,$20 ; signal 'output leading spaces' + +;; OUT-NUM-3 +L1A30: LD BC,$FC18 ; value -1000 + CALL L192A ; routine OUT-SP-NO outputs space or number + LD BC,$FF9C ; value -100 + CALL L192A ; routine OUT-SP-NO + LD C,$F6 ; value -10 ( B is still $FF ) + CALL L192A ; routine OUT-SP-NO + LD A,L ; remainder to A. + +;; OUT-NUM-4 +L1A42: CALL L15EF ; routine OUT-CODE for final digit. + ; else report code zero wouldn't get + ; printed. + POP HL ; restore the + POP DE ; registers and + RET ; return. + + +;*************************************************** +;** Part 7. BASIC LINE AND COMMAND INTERPRETATION ** +;*************************************************** + +; ---------------- +; The offset table +; ---------------- +; The BASIC interpreter has found a command code $CE - $FF +; which is then reduced to range $00 - $31 and added to the base address +; of this table to give the address of an offset which, when added to +; the offset therein, gives the location in the following parameter table +; where a list of class codes, separators and addresses relevant to the +; command exists. + +;; offst-tbl +L1A48: DEFB L1AF9 - $ ; B1 offset to Address: P-DEF-FN + DEFB L1B14 - $ ; CB offset to Address: P-CAT + DEFB L1B06 - $ ; BC offset to Address: P-FORMAT + DEFB L1B0A - $ ; BF offset to Address: P-MOVE + DEFB L1B10 - $ ; C4 offset to Address: P-ERASE + DEFB L1AFC - $ ; AF offset to Address: P-OPEN + DEFB L1B02 - $ ; B4 offset to Address: P-CLOSE + DEFB L1AE2 - $ ; 93 offset to Address: P-MERGE + DEFB L1AE1 - $ ; 91 offset to Address: P-VERIFY + DEFB L1AE3 - $ ; 92 offset to Address: P-BEEP + DEFB L1AE7 - $ ; 95 offset to Address: P-CIRCLE + DEFB L1AEB - $ ; 98 offset to Address: P-INK + DEFB L1AEC - $ ; 98 offset to Address: P-PAPER + DEFB L1AED - $ ; 98 offset to Address: P-FLASH + DEFB L1AEE - $ ; 98 offset to Address: P-BRIGHT + DEFB L1AEF - $ ; 98 offset to Address: P-INVERSE + DEFB L1AF0 - $ ; 98 offset to Address: P-OVER + DEFB L1AF1 - $ ; 98 offset to Address: P-OUT + DEFB L1AD9 - $ ; 7F offset to Address: P-LPRINT + DEFB L1ADC - $ ; 81 offset to Address: P-LLIST + DEFB L1A8A - $ ; 2E offset to Address: P-STOP + DEFB L1AC9 - $ ; 6C offset to Address: P-READ + DEFB L1ACC - $ ; 6E offset to Address: P-DATA + DEFB L1ACF - $ ; 70 offset to Address: P-RESTORE + DEFB L1AA8 - $ ; 48 offset to Address: P-NEW + DEFB L1AF5 - $ ; 94 offset to Address: P-BORDER + DEFB L1AB8 - $ ; 56 offset to Address: P-CONT + DEFB L1AA2 - $ ; 3F offset to Address: P-DIM + DEFB L1AA5 - $ ; 41 offset to Address: P-REM + DEFB L1A90 - $ ; 2B offset to Address: P-FOR + DEFB L1A7D - $ ; 17 offset to Address: P-GO-TO + DEFB L1A86 - $ ; 1F offset to Address: P-GO-SUB + DEFB L1A9F - $ ; 37 offset to Address: P-INPUT + DEFB L1AE0 - $ ; 77 offset to Address: P-LOAD + DEFB L1AAE - $ ; 44 offset to Address: P-LIST + DEFB L1A7A - $ ; 0F offset to Address: P-LET + DEFB L1AC5 - $ ; 59 offset to Address: P-PAUSE + DEFB L1A98 - $ ; 2B offset to Address: P-NEXT + DEFB L1AB1 - $ ; 43 offset to Address: P-POKE + DEFB L1A9C - $ ; 2D offset to Address: P-PRINT + DEFB L1AC1 - $ ; 51 offset to Address: P-PLOT + DEFB L1AAB - $ ; 3A offset to Address: P-RUN + DEFB L1ADF - $ ; 6D offset to Address: P-SAVE + DEFB L1AB5 - $ ; 42 offset to Address: P-RANDOM + DEFB L1A81 - $ ; 0D offset to Address: P-IF + DEFB L1ABE - $ ; 49 offset to Address: P-CLS + DEFB L1AD2 - $ ; 5C offset to Address: P-DRAW + DEFB L1ABB - $ ; 44 offset to Address: P-CLEAR + DEFB L1A8D - $ ; 15 offset to Address: P-RETURN + DEFB L1AD6 - $ ; 5D offset to Address: P-COPY + + +; ------------------------------- +; The parameter or "Syntax" table +; ------------------------------- +; For each command there exists a variable list of parameters. +; If the character is greater than a space it is a required separator. +; If less, then it is a command class in the range 00 - 0B. +; Note that classes 00, 03 and 05 will fetch the addresses from this table. +; Some classes e.g. 07 and 0B have the same address in all invocations +; and the command is re-computed from the low-byte of the parameter address. +; Some e.g. 02 are only called once so a call to the command is made from +; within the class routine rather than holding the address within the table. +; Some class routines check syntax entirely and some leave this task for the +; command itself. +; Others for example CIRCLE (x,y,z) check the first part (x,y) using the +; class routine and the final part (,z) within the command. +; The last few commands appear to have been added in a rush but their syntax +; is rather simple e.g. MOVE "M1","M2" + +;; P-LET +L1A7A: DEFB $01 ; Class-01 - A variable is required. + DEFB $3D ; Separator: '=' + DEFB $02 ; Class-02 - An expression, numeric or string, + ; must follow. + +;; P-GO-TO +L1A7D: DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1E67 ; Address: $1E67; Address: GO-TO + +;; P-IF +L1A81: DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $CB ; Separator: 'THEN' + DEFB $05 ; Class-05 - Variable syntax checked + ; by routine. + DEFW L1CF0 ; Address: $1CF0; Address: IF + +;; P-GO-SUB +L1A86: DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1EED ; Address: $1EED; Address: GO-SUB + +;; P-STOP +L1A8A: DEFB $00 ; Class-00 - No further operands. + DEFW L1CEE ; Address: $1CEE; Address: STOP + +;; P-RETURN +L1A8D: DEFB $00 ; Class-00 - No further operands. + DEFW L1F23 ; Address: $1F23; Address: RETURN + +;; P-FOR +L1A90: DEFB $04 ; Class-04 - A single character variable must + ; follow. + DEFB $3D ; Separator: '=' + DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $CC ; Separator: 'TO' + DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $05 ; Class-05 - Variable syntax checked + ; by routine. + DEFW L1D03 ; Address: $1D03; Address: FOR + +;; P-NEXT +L1A98: DEFB $04 ; Class-04 - A single character variable must + ; follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1DAB ; Address: $1DAB; Address: NEXT + +;; P-PRINT +L1A9C: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L1FCD ; Address: $1FCD; Address: PRINT + +;; P-INPUT +L1A9F: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L2089 ; Address: $2089; Address: INPUT + +;; P-DIM +L1AA2: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L2C02 ; Address: $2C02; Address: DIM + +;; P-REM +L1AA5: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L1BB2 ; Address: $1BB2; Address: REM + +;; P-NEW +L1AA8: DEFB $00 ; Class-00 - No further operands. + DEFW L11B7 ; Address: $11B7; Address: NEW + +;; P-RUN +L1AAB: DEFB $03 ; Class-03 - A numeric expression may follow + ; else default to zero. + DEFW L1EA1 ; Address: $1EA1; Address: RUN + +;; P-LIST +L1AAE: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L17F9 ; Address: $17F9; Address: LIST + +;; P-POKE +L1AB1: DEFB $08 ; Class-08 - Two comma-separated numeric + ; expressions required. + DEFB $00 ; Class-00 - No further operands. + DEFW L1E80 ; Address: $1E80; Address: POKE + +;; P-RANDOM +L1AB5: DEFB $03 ; Class-03 - A numeric expression may follow + ; else default to zero. + DEFW L1E4F ; Address: $1E4F; Address: RANDOMIZE + +;; P-CONT +L1AB8: DEFB $00 ; Class-00 - No further operands. + DEFW L1E5F ; Address: $1E5F; Address: CONTINUE + +;; P-CLEAR +L1ABB: DEFB $03 ; Class-03 - A numeric expression may follow + ; else default to zero. + DEFW L1EAC ; Address: $1EAC; Address: CLEAR + +;; P-CLS +L1ABE: DEFB $00 ; Class-00 - No further operands. + DEFW L0D6B ; Address: $0D6B; Address: CLS + +;; P-PLOT +L1AC1: DEFB $09 ; Class-09 - Two comma-separated numeric + ; expressions required with optional colour + ; items. + DEFB $00 ; Class-00 - No further operands. + DEFW L22DC ; Address: $22DC; Address: PLOT + +;; P-PAUSE +L1AC5: DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1F3A ; Address: $1F3A; Address: PAUSE + +;; P-READ +L1AC9: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L1DED ; Address: $1DED; Address: READ + +;; P-DATA +L1ACC: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L1E27 ; Address: $1E27; Address: DATA + +;; P-RESTORE +L1ACF: DEFB $03 ; Class-03 - A numeric expression may follow + ; else default to zero. + DEFW L1E42 ; Address: $1E42; Address: RESTORE + +;; P-DRAW +L1AD2: DEFB $09 ; Class-09 - Two comma-separated numeric + ; expressions required with optional colour + ; items. + DEFB $05 ; Class-05 - Variable syntax checked + ; by routine. + DEFW L2382 ; Address: $2382; Address: DRAW + +;; P-COPY +L1AD6: DEFB $00 ; Class-00 - No further operands. + DEFW L0EAC ; Address: $0EAC; Address: COPY + +;; P-LPRINT +L1AD9: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L1FC9 ; Address: $1FC9; Address: LPRINT + +;; P-LLIST +L1ADC: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L17F5 ; Address: $17F5; Address: LLIST + +;; P-SAVE +L1ADF: DEFB $0B ; Class-0B - Offset address converted to tape + ; command. + +;; P-LOAD +L1AE0: DEFB $0B ; Class-0B - Offset address converted to tape + ; command. + +;; P-VERIFY +L1AE1: DEFB $0B ; Class-0B - Offset address converted to tape + ; command. + +;; P-MERGE +L1AE2: DEFB $0B ; Class-0B - Offset address converted to tape + ; command. + +;; P-BEEP +L1AE3: DEFB $08 ; Class-08 - Two comma-separated numeric + ; expressions required. + DEFB $00 ; Class-00 - No further operands. + DEFW L03F8 ; Address: $03F8; Address: BEEP + +;; P-CIRCLE +L1AE7: DEFB $09 ; Class-09 - Two comma-separated numeric + ; expressions required with optional colour + ; items. + DEFB $05 ; Class-05 - Variable syntax checked + ; by routine. + DEFW L2320 ; Address: $2320; Address: CIRCLE + +;; P-INK +L1AEB: DEFB $07 ; Class-07 - Offset address is converted to + ; colour code. + +;; P-PAPER +L1AEC: DEFB $07 ; Class-07 - Offset address is converted to + ; colour code. + +;; P-FLASH +L1AED: DEFB $07 ; Class-07 - Offset address is converted to + ; colour code. + +;; P-BRIGHT +L1AEE: DEFB $07 ; Class-07 - Offset address is converted to + ; colour code. + +;; P-INVERSE +L1AEF: DEFB $07 ; Class-07 - Offset address is converted to + ; colour code. + +;; P-OVER +L1AF0: DEFB $07 ; Class-07 - Offset address is converted to + ; colour code. + +;; P-OUT +L1AF1: DEFB $08 ; Class-08 - Two comma-separated numeric + ; expressions required. + DEFB $00 ; Class-00 - No further operands. + DEFW L1E7A ; Address: $1E7A; Address: OUT + +;; P-BORDER +L1AF5: DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L2294 ; Address: $2294; Address: BORDER + +;; P-DEF-FN +L1AF9: DEFB $05 ; Class-05 - Variable syntax checked entirely + ; by routine. + DEFW L1F60 ; Address: $1F60; Address: DEF-FN + +;; P-OPEN +L1AFC: DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $2C ; Separator: ',' see Footnote * + DEFB $0A ; Class-0A - A string expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1736 ; Address: $1736; Address: OPEN + +;; P-CLOSE +L1B02: DEFB $06 ; Class-06 - A numeric expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L16E5 ; Address: $16E5; Address: CLOSE + +;; P-FORMAT +L1B06: DEFB $0A ; Class-0A - A string expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1793 ; Address: $1793; Address: CAT-ETC + +;; P-MOVE +L1B0A: DEFB $0A ; Class-0A - A string expression must follow. + DEFB $2C ; Separator: ',' + DEFB $0A ; Class-0A - A string expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1793 ; Address: $1793; Address: CAT-ETC + +;; P-ERASE +L1B10: DEFB $0A ; Class-0A - A string expression must follow. + DEFB $00 ; Class-00 - No further operands. + DEFW L1793 ; Address: $1793; Address: CAT-ETC + +;; P-CAT +L1B14: DEFB $00 ; Class-00 - No further operands. + DEFW L1793 ; Address: $1793; Address: CAT-ETC + +; * Note that a comma is required as a separator with the OPEN command +; but the Interface 1 programmers relaxed this allowing ';' as an +; alternative for their channels creating a confusing mixture of +; allowable syntax as it is this ROM which opens or re-opens the +; normal channels. + +; ------------------------------- +; Main parser (BASIC interpreter) +; ------------------------------- +; This routine is called once from MAIN-2 when the BASIC line is to +; be entered or re-entered into the Program area and the syntax +; requires checking. + +;; LINE-SCAN +L1B17: RES 7,(IY+$01) ; update FLAGS - signal checking syntax + CALL L19FB ; routine E-LINE-NO >> + ; fetches the line number if in range. + + XOR A ; clear the accumulator. + LD ($5C47),A ; set statement number SUBPPC to zero. + DEC A ; set accumulator to $FF. + LD ($5C3A),A ; set ERR_NR to 'OK' - 1. + JR L1B29 ; forward to continue at STMT-L-1. + +; -------------- +; Statement loop +; -------------- +; +; + +;; STMT-LOOP +L1B28: RST 20H ; NEXT-CHAR + +; -> the entry point from above or LINE-RUN +;; STMT-L-1 +L1B29: CALL L16BF ; routine SET-WORK clears workspace etc. + + INC (IY+$0D) ; increment statement number SUBPPC + JP M,L1C8A ; to REPORT-C to raise + ; 'Nonsense in BASIC' if over 127. + + RST 18H ; GET-CHAR + + LD B,$00 ; set B to zero for later indexing. + ; early so any other reason ??? + + CP $0D ; is character carriage return ? + ; i.e. an empty statement. + JR Z,L1BB3 ; forward to LINE-END if so. + + CP $3A ; is it statement end marker ':' ? + ; i.e. another type of empty statement. + JR Z,L1B28 ; back to STMT-LOOP if so. + + LD HL,L1B76 ; address: STMT-RET + PUSH HL ; is now pushed as a return address + LD C,A ; transfer the current character to C. + +; advance CH_ADD to a position after command and test if it is a command. + + RST 20H ; NEXT-CHAR to advance pointer + LD A,C ; restore current character + SUB $CE ; subtract 'DEF FN' - first command + JP C,L1C8A ; jump to REPORT-C if less than a command + ; raising + ; 'Nonsense in BASIC' + + LD C,A ; put the valid command code back in C. + ; register B is zero. + LD HL,L1A48 ; address: offst-tbl + ADD HL,BC ; index into table with one of 50 commands. + LD C,(HL) ; pick up displacement to syntax table entry. + ADD HL,BC ; add to address the relevant entry. + JR L1B55 ; forward to continue at GET-PARAM + +; ---------------------- +; The main scanning loop +; ---------------------- +; not documented properly +; + +;; SCAN-LOOP +L1B52: LD HL,($5C74) ; fetch temporary address from T_ADDR + ; during subsequent loops. + +; -> the initial entry point with HL addressing start of syntax table entry. + +;; GET-PARAM +L1B55: LD A,(HL) ; pick up the parameter. + INC HL ; address next one. + LD ($5C74),HL ; save pointer in system variable T_ADDR + + LD BC,L1B52 ; address: SCAN-LOOP + PUSH BC ; is now pushed on stack as looping address. + LD C,A ; store parameter in C. + CP $20 ; is it greater than ' ' ? + JR NC,L1B6F ; forward to SEPARATOR to check that correct + ; separator appears in statement if so. + + LD HL,L1C01 ; address: class-tbl. + LD B,$00 ; prepare to index into the class table. + ADD HL,BC ; index to find displacement to routine. + LD C,(HL) ; displacement to BC + ADD HL,BC ; add to address the CLASS routine. + PUSH HL ; push the address on the stack. + + RST 18H ; GET-CHAR - HL points to place in statement. + + DEC B ; reset the zero flag - the initial state + ; for all class routines. + + RET ; and make an indirect jump to routine + ; and then SCAN-LOOP (also on stack). + +; Note. one of the class routines will eventually drop the return address +; off the stack breaking out of the above seemingly endless loop. + +; ---------------- +; Verify separator +; ---------------- +; This routine is called once to verify that the mandatory separator +; present in the parameter table is also present in the correct +; location following the command. For example, the 'THEN' token after +; the 'IF' token and expression. + +;; SEPARATOR +L1B6F: RST 18H ; GET-CHAR + CP C ; does it match the character in C ? + JP NZ,L1C8A ; jump forward to REPORT-C if not + ; 'Nonsense in BASIC'. + + RST 20H ; NEXT-CHAR advance to next character + RET ; return. + +; ------------------------------ +; Come here after interpretation +; ------------------------------ +; +; + +;; STMT-RET +L1B76: CALL L1F54 ; routine BREAK-KEY is tested after every + ; statement. + JR C,L1B7D ; step forward to STMT-R-1 if not pressed. + +;; REPORT-L +L1B7B: RST 08H ; ERROR-1 + DEFB $14 ; Error Report: BREAK into program + +;; STMT-R-1 +L1B7D: CALL L3B4D ; Spectrum 128 patch + NOP + +L1B81: JR NZ,L1BF4 ; forward to STMT-NEXT if a program line. + + LD HL,($5C42) ; fetch line number from NEWPPC + BIT 7,H ; will be set if minus two - direct command(s) + JR Z,L1B9E ; forward to LINE-NEW if a jump is to be + ; made to a new program line/statement. + +; -------------------- +; Run a direct command +; -------------------- +; A direct command is to be run or, if continuing from above, +; the next statement of a direct command is to be considered. + +;; LINE-RUN +L1B8A: LD HL,$FFFE ; The dummy value minus two + LD ($5C45),HL ; is set/reset as line number in PPC. + LD HL,($5C61) ; point to end of line + 1 - WORKSP. + DEC HL ; now point to $80 end-marker. + LD DE,($5C59) ; address the start of line E_LINE. + DEC DE ; now location before - for GET-CHAR. + LD A,($5C44) ; load statement to A from NSPPC. + JR L1BD1 ; forward to NEXT-LINE. + +; ------------------------------ +; Find start address of new line +; ------------------------------ +; The branch was to here if a jump is to made to a new line number +; and statement. +; That is the previous statement was a GO TO, GO SUB, RUN, RETURN, NEXT etc.. + +;; LINE-NEW +L1B9E: CALL L196E ; routine LINE-ADDR gets address of line + ; returning zero flag set if line found. + LD A,($5C44) ; fetch new statement from NSPPC + JR Z,L1BBF ; forward to LINE-USE if line matched. + +; continue as must be a direct command. + + AND A ; test statement which should be zero + JR NZ,L1BEC ; forward to REPORT-N if not. + ; 'Statement lost' + +; + + LD B,A ; save statement in B. ? + LD A,(HL) ; fetch high byte of line number. + AND $C0 ; test if using direct command + ; a program line is less than $3F + LD A,B ; retrieve statement. + ; (we can assume it is zero). + JR Z,L1BBF ; forward to LINE-USE if was a program line + +; Alternatively a direct statement has finished correctly. + +;; REPORT-0 +L1BB0: RST 08H ; ERROR-1 + DEFB $FF ; Error Report: OK + +; ------------------ +; Handle REM command +; ------------------ +; The REM command routine. +; The return address STMT-RET is dropped and the rest of line ignored. + +;; REM +L1BB2: POP BC ; drop return address STMT-RET and + ; continue ignoring rest of line. + +; ------------ +; End of line? +; ------------ +; +; + +;; LINE-END +L1BB3: CALL L2530 ; routine SYNTAX-Z (UNSTACK-Z?) + RET Z ; return if checking syntax. + + LD HL,($5C55) ; fetch NXTLIN to HL. + LD A,$C0 ; test against the + AND (HL) ; system limit $3F. + RET NZ ; return if more as must be + ; end of program. + ; (or direct command) + + XOR A ; set statement to zero. + +; and continue to set up the next following line and then consider this new one. + +; --------------------- +; General line checking +; --------------------- +; The branch was here from LINE-NEW if BASIC is branching. +; or a continuation from above if dealing with a new sequential line. +; First make statement zero number one leaving others unaffected. + +;; LINE-USE +L1BBF: CP $01 ; will set carry if zero. + ADC A,$00 ; add in any carry. + + LD D,(HL) ; high byte of line number to D. + INC HL ; advance pointer. + LD E,(HL) ; low byte of line number to E. + LD ($5C45),DE ; set system variable PPC. + + INC HL ; advance pointer. + LD E,(HL) ; low byte of line length to E. + INC HL ; advance pointer. + LD D,(HL) ; high byte of line length to D. + + EX DE,HL ; swap pointer to DE before + ADD HL,DE ; adding to address the end of line. + INC HL ; advance to start of next line. + +; ----------------------------- +; Update NEXT LINE but consider +; previous line or edit line. +; ----------------------------- +; The pointer will be the next line if continuing from above or to +; edit line end-marker ($80) if from LINE-RUN. + +;; NEXT-LINE +L1BD1: LD ($5C55),HL ; store pointer in system variable NXTLIN + + EX DE,HL ; bring back pointer to previous or edit line + LD ($5C5D),HL ; and update CH_ADD with character address. + + LD D,A ; store statement in D. + LD E,$00 ; set E to zero to suppress token searching + ; if EACH-STMT is to be called. + LD (IY+$0A),$FF ; set statement NSPPC to $FF signalling + ; no jump to be made. + DEC D ; decrement and test statement + LD (IY+$0D),D ; set SUBPPC to decremented statement number. + JP Z,L1B28 ; to STMT-LOOP if result zero as statement is + ; at start of line and address is known. + + INC D ; else restore statement. + CALL L198B ; routine EACH-STMT finds the D'th statement + ; address as E does not contain a token. + JR Z,L1BF4 ; forward to STMT-NEXT if address found. + +;; REPORT-N +L1BEC: RST 08H ; ERROR-1 + DEFB $16 ; Error Report: Statement lost + +; ----------------- +; End of statement? +; ----------------- +; This combination of routines is called from 20 places when +; the end of a statement should have been reached and all preceding +; syntax is in order. + +;; CHECK-END +L1BEE: CALL L2530 ; routine SYNTAX-Z + RET NZ ; return immediately in runtime + + POP BC ; drop address of calling routine. + POP BC ; drop address STMT-RET. + ; and continue to find next statement. + +; -------------------- +; Go to next statement +; -------------------- +; Acceptable characters at this point are carriage return and ':'. +; If so go to next statement which in the first case will be on next line. + +;; STMT-NEXT +L1BF4: CALL L3B5D ; Spectrum 128 patch + +L1BF7: JR Z,L1BB3 ; back to LINE-END if so. + + CP $3A ; is it ':' ? + JP Z,L1B28 ; jump back to STMT-LOOP to consider + ; further statements + + JP L1C8A ; jump to REPORT-C with any other character + ; 'Nonsense in BASIC'. + +; Note. the two-byte sequence 'rst 08; defb $0b' could replace the above jp. + +; ------------------- +; Command class table +; ------------------- +; + +;; class-tbl +L1C01: DEFB L1C10 - $ ; 0F offset to Address: CLASS-00 + DEFB L1C1F - $ ; 1D offset to Address: CLASS-01 + DEFB L1C4E - $ ; 4B offset to Address: CLASS-02 + DEFB L1C0D - $ ; 09 offset to Address: CLASS-03 + DEFB L1C6C - $ ; 67 offset to Address: CLASS-04 + DEFB L1C11 - $ ; 0B offset to Address: CLASS-05 + DEFB L1C82 - $ ; 7B offset to Address: CLASS-06 + DEFB L1C96 - $ ; 8E offset to Address: CLASS-07 + DEFB L1C7A - $ ; 71 offset to Address: CLASS-08 + DEFB L1CBE - $ ; B4 offset to Address: CLASS-09 + DEFB L1C8C - $ ; 81 offset to Address: CLASS-0A + DEFB L1CDB - $ ; CF offset to Address: CLASS-0B + + +; -------------------------------- +; Command classes---00, 03, and 05 +; -------------------------------- +; class-03 e.g RUN or RUN 200 ; optional operand +; class-00 e.g CONTINUE ; no operand +; class-05 e.g PRINT ; variable syntax checked by routine + +;; CLASS-03 +L1C0D: CALL L1CDE ; routine FETCH-NUM + +;; CLASS-00 + +L1C10: CP A ; reset zero flag. + +; if entering here then all class routines are entered with zero reset. + +;; CLASS-05 +L1C11: POP BC ; drop address SCAN-LOOP. + CALL Z,L1BEE ; if zero set then call routine CHECK-END >>> + ; as should be no further characters. + + EX DE,HL ; save HL to DE. + LD HL,($5C74) ; fetch T_ADDR + LD C,(HL) ; fetch low byte of routine + INC HL ; address next. + LD B,(HL) ; fetch high byte of routine. + EX DE,HL ; restore HL from DE + PUSH BC ; push the address + RET ; and make an indirect jump to the command. + +; -------------------------------- +; Command classes---01, 02, and 04 +; -------------------------------- +; class-01 e.g LET A = 2*3 ; a variable is reqd + +; This class routine is also called from INPUT and READ to find the +; destination variable for an assignment. + +;; CLASS-01 +L1C1F: CALL L28B2 ; routine LOOK-VARS returns carry set if not + ; found in runtime. + +; ---------------------- +; Variable in assignment +; ---------------------- +; +; + +;; VAR-A-1 +L1C22: LD (IY+$37),$00 ; set FLAGX to zero + JR NC,L1C30 ; forward to VAR-A-2 if found or checking + ; syntax. + + SET 1,(IY+$37) ; FLAGX - Signal a new variable + JR NZ,L1C46 ; to VAR-A-3 if not assigning to an array + ; e.g. LET a$(3,3) = "X" + +;; REPORT-2 +L1C2E: RST 08H ; ERROR-1 + DEFB $01 ; Error Report: Variable not found + +;; VAR-A-2 +L1C30: CALL Z,L2996 ; routine STK-VAR considers a subscript/slice + BIT 6,(IY+$01) ; test FLAGS - Numeric or string result ? + JR NZ,L1C46 ; to VAR-A-3 if numeric + + XOR A ; default to array/slice - to be retained. + CALL L2530 ; routine SYNTAX-Z + CALL NZ,L2BF1 ; routine STK-FETCH is called in runtime + ; may overwrite A with 1. + LD HL,$5C71 ; address system variable FLAGX + OR (HL) ; set bit 0 if simple variable to be reclaimed + LD (HL),A ; update FLAGX + EX DE,HL ; start of string/subscript to DE + +;; VAR-A-3 +L1C46: LD ($5C72),BC ; update STRLEN + LD ($5C4D),HL ; and DEST of assigned string. + RET ; return. + +; ------------------------------------------------- +; class-02 e.g. LET a = 1 + 1 ; an expression must follow + +;; CLASS-02 +L1C4E: POP BC ; drop return address SCAN-LOOP + CALL L1C56 ; routine VAL-FET-1 is called to check + ; expression and assign result in runtime + CALL L1BEE ; routine CHECK-END checks nothing else + ; is present in statement. + RET ; Return + +; ------------- +; Fetch a value +; ------------- +; +; + +;; VAL-FET-1 +L1C56: LD A,($5C3B) ; initial FLAGS to A + +;; VAL-FET-2 +L1C59: PUSH AF ; save A briefly + CALL L24FB ; routine SCANNING evaluates expression. + POP AF ; restore A + LD D,(IY+$01) ; post-SCANNING FLAGS to D + XOR D ; xor the two sets of flags + AND $40 ; pick up bit 6 of xored FLAGS should be zero + JR NZ,L1C8A ; forward to REPORT-C if not zero + ; 'Nonsense in BASIC' - results don't agree. + + BIT 7,D ; test FLAGS - is syntax being checked ? + JP NZ,L2AFF ; jump forward to LET to make the assignment + ; in runtime. + + RET ; but return from here if checking syntax. + +; ------------------ +; Command class---04 +; ------------------ +; class-04 e.g. FOR i ; a single character variable must follow + +;; CLASS-04 +L1C6C: CALL L28B2 ; routine LOOK-VARS + PUSH AF ; preserve flags. + LD A,C ; fetch type - should be 011xxxxx + OR $9F ; combine with 10011111. + INC A ; test if now $FF by incrementing. + JR NZ,L1C8A ; forward to REPORT-C if result not zero. + + POP AF ; else restore flags. + JR L1C22 ; back to VAR-A-1 + + +; -------------------------------- +; Expect numeric/string expression +; -------------------------------- +; This routine is used to get the two coordinates of STRING$, ATTR and POINT. +; It is also called from PRINT-ITEM to get the two numeric expressions that +; follow the AT ( in PRINT AT, INPUT AT). + +;; NEXT-2NUM +L1C79: RST 20H ; NEXT-CHAR advance past 'AT' or '('. + +; -------- +; class-08 e.g POKE 65535,2 ; two numeric expressions separated by comma +;; CLASS-08 +;; EXPT-2NUM +L1C7A: CALL L1C82 ; routine EXPT-1NUM is called for first + ; numeric expression + CP $2C ; is character ',' ? + JR NZ,L1C8A ; to REPORT-C if not required separator. + ; 'Nonsense in BASIC'. + + RST 20H ; NEXT-CHAR + +; -> +; class-06 e.g. GOTO a*1000 ; a numeric expression must follow +;; CLASS-06 +;; EXPT-1NUM +L1C82: CALL L24FB ; routine SCANNING + BIT 6,(IY+$01) ; test FLAGS - Numeric or string result ? + RET NZ ; return if result is numeric. + +;; REPORT-C +L1C8A: RST 08H ; ERROR-1 + DEFB $0B ; Error Report: Nonsense in BASIC + +; --------------------------------------------------------------- +; class-0A e.g. ERASE "????" ; a string expression must follow. +; ; these only occur in unimplemented commands +; ; although the routine expt-exp is called +; ; from SAVE-ETC + +;; CLASS-0A +;; EXPT-EXP +L1C8C: CALL L24FB ; routine SCANNING + BIT 6,(IY+$01) ; test FLAGS - Numeric or string result ? + RET Z ; return if string result. + + JR L1C8A ; back to REPORT-C if numeric. + +; --------------------- +; Set permanent colours +; class 07 +; --------------------- +; class-07 e.g PAPER 6 ; a single class for a collection of +; ; similar commands. Clever. +; +; Note. these commands should ensure that current channel is 'S' + +;; CLASS-07 +L1C96: BIT 7,(IY+$01) ; test FLAGS - checking syntax only ? + RES 0,(IY+$02) ; update TV_FLAG - signal main screen in use + CALL NZ,L0D4D ; routine TEMPS is called in runtime. + POP AF ; drop return address SCAN-LOOP + LD A,($5C74) ; T_ADDR_lo to accumulator. + ; points to '$07' entry + 1 + ; e.g. for INK points to $EC now + +; Note if you move alter the syntax table next line may have to be altered. + +; Note. For ZASM assembler replace following expression with SUB $13. + +L1CA5: SUB low (L1AEB-$D8) ; convert $EB to $D8 ('INK') etc. + ; ( is SUB $13 in standard ROM ) + + CALL L21FC ; routine CO-TEMP-4 + CALL L1BEE ; routine CHECK-END check that nothing else + ; in statement. + +; return here in runtime. + + LD HL,($5C8F) ; pick up ATTR_T and MASK_T + LD ($5C8D),HL ; and store in ATTR_P and MASK_P + LD HL,$5C91 ; point to P_FLAG. + LD A,(HL) ; pick up in A + RLCA ; rotate to left + XOR (HL) ; combine with HL + AND $AA ; 10101010 + XOR (HL) ; only permanent bits affected + LD (HL),A ; reload into P_FLAG. + RET ; return. + +; ------------------ +; Command class---09 +; ------------------ +; e.g. PLOT PAPER 0; 128,88 ; two coordinates preceded by optional +; ; embedded colour items. +; +; Note. this command should ensure that current channel is actually 'S'. + +;; CLASS-09 +L1CBE: CALL L2530 ; routine SYNTAX-Z + JR Z,L1CD6 ; forward to CL-09-1 if checking syntax. + + RES 0,(IY+$02) ; update TV_FLAG - signal main screen in use + CALL L0D4D ; routine TEMPS is called. + LD HL,$5C90 ; point to MASK_T + LD A,(HL) ; fetch mask to accumulator. + OR $F8 ; or with 11111000 paper/bright/flash 8 + LD (HL),A ; mask back to MASK_T system variable. + RES 6,(IY+$57) ; reset P_FLAG - signal NOT PAPER 9 ? + + RST 18H ; GET-CHAR + +;; CL-09-1 +L1CD6: CALL L21E2 ; routine CO-TEMP-2 deals with any embedded + ; colour items. + JR L1C7A ; exit via EXPT-2NUM to check for x,y. + +; Note. if either of the numeric expressions contain STR$ then the flag setting +; above will be undone when the channel flags are reset during STR$. +; e.g. +; 10 BORDER 3 : PLOT VAL STR$ 128, VAL STR$ 100 +; credit John Elliott. + +; ------------------ +; Command class---0B +; ------------------ +; Again a single class for four commands. +; This command just jumps back to SAVE-ETC to handle the four tape commands. +; The routine itself works out which command has called it by examining the +; address in T_ADDR_lo. Note therefore that the syntax table has to be +; located where these and other sequential command addresses are not split +; over a page boundary. + +;; CLASS-0B +L1CDB: JP L0605 ; jump way back to SAVE-ETC + +; -------------- +; Fetch a number +; -------------- +; This routine is called from CLASS-03 when a command may be followed by +; an optional numeric expression e.g. RUN. If the end of statement has +; been reached then zero is used as the default. +; Also called from LIST-4. + +;; FETCH-NUM +L1CDE: CP $0D ; is character a carriage return ? + JR Z,L1CE6 ; forward to USE-ZERO if so + + CP $3A ; is it ':' ? + JR NZ,L1C82 ; forward to EXPT-1NUM if not. + ; else continue and use zero. + +; ---------------- +; Use zero routine +; ---------------- +; This routine is called four times to place the value zero on the +; calculator stack as a default value in runtime. + +;; USE-ZERO +L1CE6: CALL L2530 ; routine SYNTAX-Z (UNSTACK-Z?) + RET Z ; + + RST 28H ;; FP-CALC + DEFB $A0 ;;stk-zero ;0. + DEFB $38 ;;end-calc + + RET ; return. + +; ------------------- +; Handle STOP command +; ------------------- +; Command Syntax: STOP +; One of the shortest and least used commands. As with 'OK' not an error. + +;; REPORT-9 +;; STOP +L1CEE: RST 08H ; ERROR-1 + DEFB $08 ; Error Report: STOP statement + +; ----------------- +; Handle IF command +; ----------------- +; e.g. IF score>100 THEN PRINT "You Win" +; The parser has already checked the expression the result of which is on +; the calculator stack. The presence of the 'THEN' separator has also been +; checked and CH-ADD points to the command after THEN. +; + +;; IF +L1CF0: POP BC ; drop return address - STMT-RET + CALL L2530 ; routine SYNTAX-Z + JR Z,L1D00 ; forward to IF-1 if checking syntax + ; to check syntax of PRINT "You Win" + + + RST 28H ;; FP-CALC score>100 (1=TRUE 0=FALSE) + DEFB $02 ;;delete . + DEFB $38 ;;end-calc + + EX DE,HL ; make HL point to deleted value + CALL L34E9 ; routine TEST-ZERO + JP C,L1BB3 ; jump to LINE-END if FALSE (0) + +;; IF-1 +L1D00: JP L1B29 ; to STMT-L-1, if true (1) to execute command + ; after 'THEN' token. + +; ------------------ +; Handle FOR command +; ------------------ +; e.g. FOR i = 0 TO 1 STEP 0.1 +; Using the syntax tables, the parser has already checked for a start and +; limit value and also for the intervening separator. +; the two values v,l are on the calculator stack. +; CLASS-04 has also checked the variable and the name is in STRLEN_lo. +; The routine begins by checking for an optional STEP. + +;; FOR +L1D03: CP $CD ; is there a 'STEP' ? + JR NZ,L1D10 ; to F-USE-1 if not to use 1 as default. + + RST 20H ; NEXT-CHAR + CALL L1C82 ; routine EXPT-1NUM + CALL L1BEE ; routine CHECK-END + JR L1D16 ; to F-REORDER + +; --- + +;; F-USE-1 +L1D10: CALL L1BEE ; routine CHECK-END + + RST 28H ;; FP-CALC v,l. + DEFB $A1 ;;stk-one v,l,1=s. + DEFB $38 ;;end-calc + + +;; F-REORDER +L1D16: RST 28H ;; FP-CALC v,l,s. + DEFB $C0 ;;st-mem-0 v,l,s. + DEFB $02 ;;delete v,l. + DEFB $01 ;;exchange l,v. + DEFB $E0 ;;get-mem-0 l,v,s. + DEFB $01 ;;exchange l,s,v. + DEFB $38 ;;end-calc + + CALL L2AFF ; routine LET assigns the initial value v to + ; the variable altering type if necessary. + LD ($5C68),HL ; The system variable MEM is made to point to + ; the variable instead of its normal + ; location MEMBOT + DEC HL ; point to single-character name + LD A,(HL) ; fetch name + SET 7,(HL) ; set bit 7 at location + LD BC,$0006 ; add six to HL + ADD HL,BC ; to address where limit should be. + RLCA ; test bit 7 of original name. + JR C,L1D34 ; forward to F-L-S if already a FOR/NEXT + ; variable + + LD C,$0D ; otherwise an additional 13 bytes are needed. + ; 5 for each value, two for line number and + ; 1 byte for looping statement. + CALL L1655 ; routine MAKE-ROOM creates them. + INC HL ; make HL address limit. + +;; F-L-S +L1D34: PUSH HL ; save position. + + RST 28H ;; FP-CALC l,s. + DEFB $02 ;;delete l. + DEFB $02 ;;delete . + DEFB $38 ;;end-calc + ; DE points to STKEND, l. + + POP HL ; restore variable position + EX DE,HL ; swap pointers + LD C,$0A ; ten bytes to move + LDIR ; Copy 'deleted' values to variable. + LD HL,($5C45) ; Load with current line number from PPC + EX DE,HL ; exchange pointers. + LD (HL),E ; save the looping line + INC HL ; in the next + LD (HL),D ; two locations. + LD D,(IY+$0D) ; fetch statement from SUBPPC system variable. + INC D ; increment statement. + INC HL ; and pointer + LD (HL),D ; and store the looping statement. + ; + CALL L1DDA ; routine NEXT-LOOP considers an initial + RET NC ; iteration. Return to STMT-RET if a loop is + ; possible to execute next statement. + +; no loop is possible so execution continues after the matching 'NEXT' + + LD B,(IY+$38) ; get single-character name from STRLEN_lo + LD HL,($5C45) ; get the current line from PPC + LD ($5C42),HL ; and store it in NEWPPC + LD A,($5C47) ; fetch current statement from SUBPPC + NEG ; Negate as counter decrements from zero + ; initially and we are in the middle of a + ; line. + LD D,A ; Store result in D. + LD HL,($5C5D) ; get current address from CH_ADD + LD E,$F3 ; search will be for token 'NEXT' + +;; F-LOOP +L1D64: PUSH BC ; save variable name. + LD BC,($5C55) ; fetch NXTLIN + CALL L1D86 ; routine LOOK-PROG searches for 'NEXT' token. + LD ($5C55),BC ; update NXTLIN + POP BC ; and fetch the letter + JR C,L1D84 ; forward to REPORT-I if the end of program + ; was reached by LOOK-PROG. + ; 'FOR without NEXT' + + RST 20H ; NEXT-CHAR fetches character after NEXT + OR $20 ; ensure it is upper-case. + CP B ; compare with FOR variable name + JR Z,L1D7C ; forward to F-FOUND if it matches. + +; but if no match i.e. nested FOR/NEXT loops then continue search. + + RST 20H ; NEXT-CHAR + JR L1D64 ; back to F-LOOP + +; --- + + +;; F-FOUND +L1D7C: RST 20H ; NEXT-CHAR + LD A,$01 ; subtract the negated counter from 1 + SUB D ; to give the statement after the NEXT + LD ($5C44),A ; set system variable NSPPC + RET ; return to STMT-RET to branch to new + ; line and statement. -> +; --- + +;; REPORT-I +L1D84: RST 08H ; ERROR-1 + DEFB $11 ; Error Report: FOR without NEXT + +; --------- +; LOOK-PROG +; --------- +; Find DATA, DEF FN or NEXT. +; This routine searches the program area for one of the above three keywords. +; On entry, HL points to start of search area. +; The token is in E, and D holds a statement count, decremented from zero. + +;; LOOK-PROG +L1D86: LD A,(HL) ; fetch current character + CP $3A ; is it ':' a statement separator ? + JR Z,L1DA3 ; forward to LOOK-P-2 if so. + +; The starting point was PROG - 1 or the end of a line. + +;; LOOK-P-1 +L1D8B: INC HL ; increment pointer to address + LD A,(HL) ; the high byte of line number + AND $C0 ; test for program end marker $80 or a + ; variable + SCF ; Set Carry Flag + RET NZ ; return with carry set if at end + ; of program. -> + + LD B,(HL) ; high byte of line number to B + INC HL ; + LD C,(HL) ; low byte to C. + LD ($5C42),BC ; set system variable NEWPPC. + INC HL ; + LD C,(HL) ; low byte of line length to C. + INC HL ; + LD B,(HL) ; high byte to B. + PUSH HL ; save address + ADD HL,BC ; add length to position. + LD B,H ; and save result + LD C,L ; in BC. + POP HL ; restore address. + LD D,$00 ; initialize statement counter to zero. + +;; LOOK-P-2 +L1DA3: PUSH BC ; save address of next line + CALL L198B ; routine EACH-STMT searches current line. + POP BC ; restore address. + RET NC ; return if match was found. -> + + JR L1D8B ; back to LOOK-P-1 for next line. + +; ------------------- +; Handle NEXT command +; ------------------- +; e.g. NEXT i +; The parameter tables have already evaluated the presence of a variable + +;; NEXT +L1DAB: BIT 1,(IY+$37) ; test FLAGX - handling a new variable ? + JP NZ,L1C2E ; jump back to REPORT-2 if so + ; 'Variable not found' + +; now test if found variable is a simple variable uninitialized by a FOR. + + LD HL,($5C4D) ; load address of variable from DEST + BIT 7,(HL) ; is it correct type ? + JR Z,L1DD8 ; forward to REPORT-1 if not + ; 'NEXT without FOR' + + INC HL ; step past variable name + LD ($5C68),HL ; and set MEM to point to three 5-byte values + ; value, limit, step. + + RST 28H ;; FP-CALC add step and re-store + DEFB $E0 ;;get-mem-0 v. + DEFB $E2 ;;get-mem-2 v,s. + DEFB $0F ;;addition v+s. + DEFB $C0 ;;st-mem-0 v+s. + DEFB $02 ;;delete . + DEFB $38 ;;end-calc + + CALL L1DDA ; routine NEXT-LOOP tests against limit. + RET C ; return if no more iterations possible. + + LD HL,($5C68) ; find start of variable contents from MEM. + LD DE,$000F ; add 3*5 to + ADD HL,DE ; address the looping line number + LD E,(HL) ; low byte to E + INC HL ; + LD D,(HL) ; high byte to D + INC HL ; address looping statement + LD H,(HL) ; and store in H + EX DE,HL ; swap registers + JP L1E73 ; exit via GO-TO-2 to execute another loop. + +; --- + +;; REPORT-1 +L1DD8: RST 08H ; ERROR-1 + DEFB $00 ; Error Report: NEXT without FOR + + +; ----------------- +; Perform NEXT loop +; ----------------- +; This routine is called from the FOR command to test for an initial +; iteration and from the NEXT command to test for all subsequent iterations. +; the system variable MEM addresses the variable's contents which, in the +; latter case, have had the step, possibly negative, added to the value. + +;; NEXT-LOOP +L1DDA: RST 28H ;; FP-CALC + DEFB $E1 ;;get-mem-1 l. + DEFB $E0 ;;get-mem-0 l,v. + DEFB $E2 ;;get-mem-2 l,v,s. + DEFB $36 ;;less-0 l,v,(1/0) negative step ? + DEFB $00 ;;jump-true l,v.(1/0) + + DEFB $02 ;;to L1DE2, NEXT-1 if step negative + + DEFB $01 ;;exchange v,l. + +;; NEXT-1 +L1DE2: DEFB $03 ;;subtract l-v OR v-l. + DEFB $37 ;;greater-0 (1/0) + DEFB $00 ;;jump-true . + + DEFB $04 ;;to L1DE9, NEXT-2 if no more iterations. + + DEFB $38 ;;end-calc . + + AND A ; clear carry flag signalling another loop. + RET ; return + +; --- + +;; NEXT-2 +L1DE9: DEFB $38 ;;end-calc . + + SCF ; set carry flag signalling looping exhausted. + RET ; return + + +; ------------------- +; Handle READ command +; ------------------- +; e.g. READ a, b$, c$(1000 TO 3000) +; A list of comma-separated variables is assigned from a list of +; comma-separated expressions. +; As it moves along the first list, the character address CH_ADD is stored +; in X_PTR while CH_ADD is used to read the second list. + +;; READ-3 +L1DEC: RST 20H ; NEXT-CHAR + +; -> Entry point. +;; READ +L1DED: CALL L1C1F ; routine CLASS-01 checks variable. + CALL L2530 ; routine SYNTAX-Z + JR Z,L1E1E ; forward to READ-2 if checking syntax + + + RST 18H ; GET-CHAR + LD ($5C5F),HL ; save character position in X_PTR. + LD HL,($5C57) ; load HL with Data Address DATADD, which is + ; the start of the program or the address + ; after the last expression that was read or + ; the address of the line number of the + ; last RESTORE command. + LD A,(HL) ; fetch character + CP $2C ; is it a comma ? + JR Z,L1E0A ; forward to READ-1 if so. + +; else all data in this statement has been read so look for next DATA token + + LD E,$E4 ; token 'DATA' + CALL L1D86 ; routine LOOK-PROG + JR NC,L1E0A ; forward to READ-1 if DATA found + +; else report the error. + +;; REPORT-E +L1E08: RST 08H ; ERROR-1 + DEFB $0D ; Error Report: Out of DATA + +;; READ-1 +L1E0A: CALL L0077 ; routine TEMP-PTR1 advances updating CH_ADD + ; with new DATADD position. + CALL L1C56 ; routine VAL-FET-1 assigns value to variable + ; checking type match and adjusting CH_ADD. + + RST 18H ; GET-CHAR fetches adjusted character position + LD ($5C57),HL ; store back in DATADD + LD HL,($5C5F) ; fetch X_PTR the original READ CH_ADD + LD (IY+$26),$00 ; now nullify X_PTR_hi + CALL L0078 ; routine TEMP-PTR2 restores READ CH_ADD + +;; READ-2 +L1E1E: RST 18H ; GET-CHAR + CP $2C ; is it ',' indicating more variables to read ? + JR Z,L1DEC ; back to READ-3 if so + + CALL L1BEE ; routine CHECK-END + RET ; return from here in runtime to STMT-RET. + +; ------------------- +; Handle DATA command +; ------------------- +; In runtime this 'command' is passed by but the syntax is checked when such +; a statement is found while parsing a line. +; e.g. DATA 1, 2, "text", score-1, a$(location, room, object), FN r(49), +; wages - tax, TRUE, The meaning of life + +;; DATA +L1E27: CALL L2530 ; routine SYNTAX-Z to check status + JR NZ,L1E37 ; forward to DATA-2 if in runtime + +;; DATA-1 +L1E2C: CALL L24FB ; routine SCANNING to check syntax of + ; expression + CP $2C ; is it a comma ? + CALL NZ,L1BEE ; routine CHECK-END checks that statement + ; is complete. Will make an early exit if + ; so. >>> + RST 20H ; NEXT-CHAR + JR L1E2C ; back to DATA-1 + +; --- + +;; DATA-2 +L1E37: LD A,$E4 ; set token to 'DATA' and continue into + ; the the PASS-BY routine. + + +; ---------------------------------- +; Check statement for DATA or DEF FN +; ---------------------------------- +; This routine is used to backtrack to a command token and then +; forward to the next statement in runtime. + +;; PASS-BY +L1E39: LD B,A ; Give BC enough space to find token. + CPDR ; Compare decrement and repeat. (Only use). + ; Work backwards till keyword is found which + ; is start of statement before any quotes. + ; HL points to location before keyword. + LD DE,$0200 ; count 1+1 statements, dummy value in E to + ; inhibit searching for a token. + JP L198B ; to EACH-STMT to find next statement + +; ----------------------------------------------------------------------- +; A General Note on Invalid Line Numbers. +; ======================================= +; One of the revolutionary concepts of Sinclair BASIC was that it supported +; virtual line numbers. That is the destination of a GO TO, RESTORE etc. need +; not exist. It could be a point before or after an actual line number. +; Zero suffices for a before but the after should logically be infinity. +; Since the maximum actual line limit is 9999 then the system limit, 16383 +; when variables kick in, would serve fine as a virtual end point. +; However, ironically, only the LOAD command gets it right. It will not +; autostart a program that has been saved with a line higher than 16383. +; All the other commands deal with the limit unsatisfactorily. +; LIST, RUN, GO TO, GO SUB and RESTORE have problems and the latter may +; crash the machine when supplied with an inappropriate virtual line number. +; This is puzzling as very careful consideration must have been given to +; this point when the new variable types were allocated their masks and also +; when the routine NEXT-ONE was successfully re-written to reflect this. +; An enigma. +; ------------------------------------------------------------------------- + +; ---------------------- +; Handle RESTORE command +; ---------------------- +; The restore command sets the system variable for the data address to +; point to the location before the supplied line number or first line +; thereafter. +; This alters the position where subsequent READ commands look for data. +; Note. If supplied with inappropriate high numbers the system may crash +; in the LINE-ADDR routine as it will pass the program/variables end-marker +; and then lose control of what it is looking for - variable or line number. +; - observation, Steven Vickers, 1984, Pitman. + +;; RESTORE +L1E42: CALL L1E99 ; routine FIND-INT2 puts integer in BC. + ; Note. B should be checked against limit $3F + ; and an error generated if higher. + +; this entry point is used from RUN command with BC holding zero + +;; REST-RUN +L1E45: LD H,B ; transfer the line + LD L,C ; number to the HL register. + CALL L196E ; routine LINE-ADDR to fetch the address. + DEC HL ; point to the location before the line. + LD ($5C57),HL ; update system variable DATADD. + RET ; return to STMT-RET (or RUN) + +; ------------------------ +; Handle RANDOMIZE command +; ------------------------ +; This command sets the SEED for the RND function to a fixed value. +; With the parameter zero, a random start point is used depending on +; how long the computer has been switched on. + +;; RANDOMIZE +L1E4F: CALL L1E99 ; routine FIND-INT2 puts parameter in BC. + LD A,B ; test this + OR C ; for zero. + JR NZ,L1E5A ; forward to RAND-1 if not zero. + + LD BC,($5C78) ; use the lower two bytes at FRAMES1. + +;; RAND-1 +L1E5A: LD ($5C76),BC ; place in SEED system variable. + RET ; return to STMT-RET + +; ----------------------- +; Handle CONTINUE command +; ----------------------- +; The CONTINUE command transfers the OLD (but incremented) values of +; line number and statement to the equivalent "NEW VALUE" system variables +; by using the last part of GO TO and exits indirectly to STMT-RET. + +;; CONTINUE +L1E5F: LD HL,($5C6E) ; fetch OLDPPC line number. + LD D,(IY+$36) ; fetch OSPPC statement. + JR L1E73 ; forward to GO-TO-2 + +; -------------------- +; Handle GO TO command +; -------------------- +; The GO TO command routine is also called by GO SUB and RUN routines +; to evaluate the parameters of both commands. +; It updates the system variables used to fetch the next line/statement. +; It is at STMT-RET that the actual change in control takes place. +; Unlike some BASICs the line number need not exist. +; Note. the high byte of the line number is incorrectly compared with $F0 +; instead of $3F. This leads to commands with operands greater than 32767 +; being considered as having been run from the editing area and the +; error report 'Statement Lost' is given instead of 'OK'. +; - Steven Vickers, 1984. + +;; GO-TO +L1E67: CALL L1E99 ; routine FIND-INT2 puts operand in BC + LD H,B ; transfer line + LD L,C ; number to HL. + LD D,$00 ; set statement to 0 - first. + LD A,H ; compare high byte only + CP $F0 ; to $F0 i.e. 61439 in full. + JR NC,L1E9F ; forward to REPORT-B if above. + +; This call entry point is used to update the system variables e.g. by RETURN. + +;; GO-TO-2 +L1E73: LD ($5C42),HL ; save line number in NEWPPC + LD (IY+$0A),D ; and statement in NSPPC + RET ; to STMT-RET (or GO-SUB command) + +; ------------------ +; Handle OUT command +; ------------------ +; Syntax has been checked and the two comma-separated values are on the +; calculator stack. + +;; OUT +L1E7A: CALL L1E85 ; routine TWO-PARAM fetches values + ; to BC and A. + OUT (C),A ; perform the operation. + RET ; return to STMT-RET. + +; ------------------- +; Handle POKE command +; ------------------- +; This routine alters a single byte in the 64K address space. +; Happily no check is made as to whether ROM or RAM is addressed. +; Sinclair BASIC requires no poking of system variables. + +;; POKE +L1E80: CALL L1E85 ; routine TWO-PARAM fetches values + ; to BC and A. + LD (BC),A ; load memory location with A. + RET ; return to STMT-RET. + +; ------------------------------------ +; Fetch two parameters from calculator stack +; ------------------------------------ +; This routine fetches a byte and word from the calculator stack +; producing an error if either is out of range. + +;; TWO-PARAM +L1E85: CALL L2DD5 ; routine FP-TO-A + JR C,L1E9F ; forward to REPORT-B if overflow occurred + + JR Z,L1E8E ; forward to TWO-P-1 if positive + + NEG ; negative numbers are made positive + +;; TWO-P-1 +L1E8E: PUSH AF ; save the value + CALL L1E99 ; routine FIND-INT2 gets integer to BC + POP AF ; restore the value + RET ; return + +; ------------- +; Find integers +; ------------- +; The first of these routines fetches a 8-bit integer (range 0-255) from the +; calculator stack to the accumulator and is used for colours, streams, +; durations and coordinates. +; The second routine fetches 16-bit integers to the BC register pair +; and is used to fetch command and function arguments involving line numbers +; or memory addresses and also array subscripts and tab arguments. +; -> + +;; FIND-INT1 +L1E94: CALL L2DD5 ; routine FP-TO-A + JR L1E9C ; forward to FIND-I-1 for common exit routine. + +; --- + +; -> + +;; FIND-INT2 +L1E99: CALL L2DA2 ; routine FP-TO-BC + +;; FIND-I-1 +L1E9C: JR C,L1E9F ; to REPORT-Bb with overflow. + + RET Z ; return if positive. + + +;; REPORT-Bb +L1E9F: RST 08H ; ERROR-1 + DEFB $0A ; Error Report: Integer out of range + +; ------------------ +; Handle RUN command +; ------------------ +; This command runs a program starting at an optional line. +; It performs a 'RESTORE 0' then CLEAR + +;; RUN +L1EA1: CALL L1E67 ; routine GO-TO puts line number in + ; system variables. + LD BC,$0000 ; prepare to set DATADD to first line. + CALL L1E45 ; routine REST-RUN does the 'restore'. + ; Note BC still holds zero. + JR L1EAF ; forward to CLEAR-RUN to clear variables + ; without disturbing RAMTOP and + ; exit indirectly to STMT-RET + +; -------------------- +; Handle CLEAR command +; -------------------- +; This command reclaims the space used by the variables. +; It also clears the screen and the GO SUB stack. +; With an integer expression, it sets the uppermost memory +; address within the BASIC system. +; "Contrary to the manual, CLEAR doesn't execute a RESTORE" - +; Steven Vickers, Pitman Pocket Guide to the Spectrum, 1984. + +;; CLEAR +L1EAC: CALL L1E99 ; routine FIND-INT2 fetches to BC. + +;; CLEAR-RUN +L1EAF: LD A,B ; test for + OR C ; zero. + JR NZ,L1EB7 ; skip to CLEAR-1 if not zero. + + LD BC,($5CB2) ; use the existing value of RAMTOP if zero. + +;; CLEAR-1 +L1EB7: PUSH BC ; save ramtop value. + + LD DE,($5C4B) ; fetch VARS + LD HL,($5C59) ; fetch E_LINE + DEC HL ; adjust to point at variables end-marker. + CALL L19E5 ; routine RECLAIM-1 reclaims the space used by + ; the variables. + CALL L0D6B ; routine CLS to clear screen. + LD HL,($5C65) ; fetch STKEND the start of free memory. + LD DE,$0032 ; allow for another 50 bytes. + ADD HL,DE ; add the overhead to HL. + + POP DE ; restore the ramtop value. + SBC HL,DE ; if HL is greater than the value then jump + JR NC,L1EDA ; forward to REPORT-M + ; 'RAMTOP no good' + + LD HL,($5CB4) ; now P-RAMT ($7FFF on 16K RAM machine) + AND A ; exact this time. + SBC HL,DE ; new ramtop must be lower or the same. + JR NC,L1EDC ; skip to CLEAR-2 if in actual RAM. + +;; REPORT-M +L1EDA: RST 08H ; ERROR-1 + DEFB $15 ; Error Report: RAMTOP no good + +;; CLEAR-2 +L1EDC: EX DE,HL ; transfer ramtop value to HL. + LD ($5CB2),HL ; update system variable RAMTOP. + POP DE ; pop the return address STMT-RET. + POP BC ; pop the Error Address. + LD (HL),$3E ; now put the GO SUB end-marker at RAMTOP. + DEC HL ; leave a location beneath it. + LD SP,HL ; initialize the machine stack pointer. + PUSH BC ; push the error address. + LD ($5C3D),SP ; make ERR_SP point to location. + EX DE,HL ; put STMT-RET in HL. + JP (HL) ; and go there directly. + +; --------------------- +; Handle GO SUB command +; --------------------- +; The GO SUB command diverts BASIC control to a new line number +; in a very similar manner to GO TO but +; the current line number and current statement + 1 +; are placed on the GO SUB stack as a RETURN point. + +;; GO-SUB +L1EED: POP DE ; drop the address STMT-RET + LD H,(IY+$0D) ; fetch statement from SUBPPC and + INC H ; increment it + EX (SP),HL ; swap - error address to HL, + ; H (statement) at top of stack, + ; L (unimportant) beneath. + INC SP ; adjust to overwrite unimportant byte + LD BC,($5C45) ; fetch the current line number from PPC + PUSH BC ; and PUSH onto GO SUB stack. + ; the empty machine-stack can be rebuilt + PUSH HL ; push the error address. + LD ($5C3D),SP ; make system variable ERR_SP point to it. + PUSH DE ; push the address STMT-RET. + CALL L1E67 ; call routine GO-TO to update the system + ; variables NEWPPC and NSPPC. + ; then make an indirect exit to STMT-RET via + LD BC,$0014 ; a 20-byte overhead memory check. + +; ---------------------- +; Check available memory +; ---------------------- +; This routine is used on many occasions when extending a dynamic area +; upwards or the GO SUB stack downwards. + +;; TEST-ROOM +L1F05: LD HL,($5C65) ; fetch STKEND + ADD HL,BC ; add the supplied test value + JR C,L1F15 ; forward to REPORT-4 if over $FFFF + + EX DE,HL ; was less so transfer to DE + LD HL,$0050 ; test against another 80 bytes + ADD HL,DE ; anyway + JR C,L1F15 ; forward to REPORT-4 if this passes $FFFF + + SBC HL,SP ; if less than the machine stack pointer + RET C ; then return - OK. + +;; REPORT-4 +L1F15: LD L,$03 ; prepare 'Out of Memory' + JP L0055 ; jump back to ERROR-3 at $0055 + ; Note. this error can't be trapped at $0008 + +; ------------------------------ +; THE 'FREE MEMORY' USER ROUTINE +; ------------------------------ +; This routine is not used by the ROM but allows users to evaluate +; approximate free memory with PRINT 65536 - USR 7962. + +;; free-mem +L1F1A: LD BC,$0000 ; allow no overhead. + + CALL L1F05 ; routine TEST-ROOM. + + LD B,H ; transfer the result + LD C,L ; to the BC register. + RET ; the USR function returns value of BC. + +; -------------------- +; THE 'RETURN' COMMAND +; -------------------- +; As with any command, there are two values on the machine stack at the time +; it is invoked. The machine stack is below the GOSUB stack. Both grow +; downwards, the machine stack by two bytes, the GOSUB stack by 3 bytes. +; The highest location is a statement byte followed by a two-byte line number. + +;; RETURN +L1F23: POP BC ; drop the address STMT-RET. + POP HL ; now the error address. + POP DE ; now a possible BASIC return line. + LD A,D ; the high byte $00 - $27 is + CP $3E ; compared with the traditional end-marker $3E. + JR Z,L1F36 ; forward to REPORT-7 with a match. + ; 'RETURN without GOSUB' + +; It was not the end-marker so a single statement byte remains at the base of +; the calculator stack. It can't be popped off. + + DEC SP ; adjust stack pointer to create room for two + ; bytes. + EX (SP),HL ; statement to H, error address to base of + ; new machine stack. + EX DE,HL ; statement to D, BASIC line number to HL. + LD ($5C3D),SP ; adjust ERR_SP to point to new stack pointer + PUSH BC ; now re-stack the address STMT-RET + JP L1E73 ; to GO-TO-2 to update statement and line + ; system variables and exit indirectly to the + ; address just pushed on stack. + +; --- + +;; REPORT-7 +L1F36: PUSH DE ; replace the end-marker. + PUSH HL ; now restore the error address + ; as will be required in a few clock cycles. + + RST 08H ; ERROR-1 + DEFB $06 ; Error Report: RETURN without GOSUB + +; -------------------- +; Handle PAUSE command +; -------------------- +; The pause command takes as its parameter the number of interrupts +; for which to wait. PAUSE 50 pauses for about a second. +; PAUSE 0 pauses indefinitely. +; Both forms can be finished by pressing a key. + +;; PAUSE +L1F3A: CALL L1E99 ; routine FIND-INT2 puts value in BC + +;; PAUSE-1 +L1F3D: HALT ; wait for interrupt. + DEC BC ; decrease counter. + LD A,B ; test if + OR C ; result is zero. + JR Z,L1F4F ; forward to PAUSE-END if so. + + LD A,B ; test if + AND C ; now $FFFF + INC A ; that is, initially zero. + JR NZ,L1F49 ; skip forward to PAUSE-2 if not. + + INC BC ; restore counter to zero. + +;; PAUSE-2 +L1F49: BIT 5,(IY+$01) ; test FLAGS - has a new key been pressed ? + JR Z,L1F3D ; back to PAUSE-1 if not. + +;; PAUSE-END +L1F4F: RES 5,(IY+$01) ; update FLAGS - signal no new key + RET ; and return. + +; ------------------- +; Check for BREAK key +; ------------------- +; This routine is called from COPY-LINE, when interrupts are disabled, +; to test if BREAK (SHIFT - SPACE) is being pressed. +; It is also called at STMT-RET after every statement. + +;; BREAK-KEY +L1F54: LD A,$7F ; Input address: $7FFE + IN A,($FE) ; read lower right keys + RRA ; rotate bit 0 - SPACE + RET C ; return if not reset + + LD A,$FE ; Input address: $FEFE + IN A,($FE) ; read lower left keys + RRA ; rotate bit 0 - SHIFT + RET ; carry will be set if not pressed. + ; return with no carry if both keys + ; pressed. + +; --------------------- +; Handle DEF FN command +; --------------------- +; e.g DEF FN r$(a$,a) = a$(a TO ) +; this 'command' is ignored in runtime but has its syntax checked +; during line-entry. + +;; DEF-FN +L1F60: CALL L2530 ; routine SYNTAX-Z + JR Z,L1F6A ; forward to DEF-FN-1 if parsing + + LD A,$CE ; else load A with 'DEF FN' and + JP L1E39 ; jump back to PASS-BY + +; --- + +; continue here if checking syntax. + +;; DEF-FN-1 +L1F6A: SET 6,(IY+$01) ; set FLAGS - Assume numeric result + CALL L2C8D ; call routine ALPHA + JR NC,L1F89 ; if not then to DEF-FN-4 to jump to + ; 'Nonsense in BASIC' + + + RST 20H ; NEXT-CHAR + CP $24 ; is it '$' ? + JR NZ,L1F7D ; to DEF-FN-2 if not as numeric. + + RES 6,(IY+$01) ; set FLAGS - Signal string result + + RST 20H ; get NEXT-CHAR + +;; DEF-FN-2 +L1F7D: CP $28 ; is it '(' ? + JR NZ,L1FBD ; to DEF-FN-7 'Nonsense in BASIC' + + + RST 20H ; NEXT-CHAR + CP $29 ; is it ')' ? + JR Z,L1FA6 ; to DEF-FN-6 if null argument + +;; DEF-FN-3 +L1F86: CALL L2C8D ; routine ALPHA checks that it is the expected + ; alphabetic character. + +;; DEF-FN-4 +L1F89: JP NC,L1C8A ; to REPORT-C if not + ; 'Nonsense in BASIC'. + + EX DE,HL ; save pointer in DE + + RST 20H ; NEXT-CHAR re-initializes HL from CH_ADD + ; and advances. + CP $24 ; '$' ? is it a string argument. + JR NZ,L1F94 ; forward to DEF-FN-5 if not. + + EX DE,HL ; save pointer to '$' in DE + + RST 20H ; NEXT-CHAR re-initializes HL and advances + +;; DEF-FN-5 +L1F94: EX DE,HL ; bring back pointer. + LD BC,$0006 ; the function requires six hidden bytes for + ; each parameter passed. + ; The first byte will be $0E + ; then 5-byte numeric value + ; or 5-byte string pointer. + + CALL L1655 ; routine MAKE-ROOM creates space in program + ; area. + + INC HL ; adjust HL (set by LDDR) + INC HL ; to point to first location. + LD (HL),$0E ; insert the 'hidden' marker. + +; Note. these invisible storage locations hold nothing meaningful for the +; moment. They will be used every time the corresponding function is +; evaluated in runtime. +; Now consider the following character fetched earlier. + + CP $2C ; is it ',' ? (more than one parameter) + JR NZ,L1FA6 ; to DEF-FN-6 if not + + + RST 20H ; else NEXT-CHAR + JR L1F86 ; and back to DEF-FN-3 + +; --- + +;; DEF-FN-6 +L1FA6: CP $29 ; should close with a ')' + JR NZ,L1FBD ; to DEF-FN-7 if not + ; 'Nonsense in BASIC' + + + RST 20H ; get NEXT-CHAR + CP $3D ; is it '=' ? + JR NZ,L1FBD ; to DEF-FN-7 if not 'Nonsense...' + + + RST 20H ; address NEXT-CHAR + LD A,($5C3B) ; get FLAGS which has been set above + PUSH AF ; and preserve + + CALL L24FB ; routine SCANNING checks syntax of expression + ; and also sets flags. + + POP AF ; restore previous flags + XOR (IY+$01) ; xor with FLAGS - bit 6 should be same + ; therefore will be reset. + AND $40 ; isolate bit 6. + +;; DEF-FN-7 +L1FBD: JP NZ,L1C8A ; jump back to REPORT-C if the expected result + ; is not the same type. + ; 'Nonsense in BASIC' + + CALL L1BEE ; routine CHECK-END will return early if + ; at end of statement and move onto next + ; else produce error report. >>> + + ; There will be no return to here. + +; ------------------------------- +; Returning early from subroutine +; ------------------------------- +; All routines are capable of being run in two modes - syntax checking mode +; and runtime mode. This routine is called often to allow a routine to return +; early if checking syntax. + +;; UNSTACK-Z +L1FC3: CALL L2530 ; routine SYNTAX-Z sets zero flag if syntax + ; is being checked. + + POP HL ; drop the return address. + RET Z ; return to previous call in chain if checking + ; syntax. + + JP (HL) ; jump to return address as BASIC program is + ; actually running. + +; --------------------- +; Handle LPRINT command +; --------------------- +; A simple form of 'PRINT #3' although it can output to 16 streams. +; Probably for compatibility with other BASICs particularly ZX81 BASIC. +; An extra UDG might have been better. + +;; LPRINT +L1FC9: LD A,$03 ; the printer channel + JR L1FCF ; forward to PRINT-1 + +; --------------------- +; Handle PRINT commands +; --------------------- +; The Spectrum's main stream output command. +; The default stream is stream 2 which is normally the upper screen +; of the computer. However the stream can be altered in range 0 - 15. + +;; PRINT +L1FCD: LD A,$02 ; the stream for the upper screen. + +; The LPRINT command joins here. + +;; PRINT-1 +L1FCF: CALL L2530 ; routine SYNTAX-Z checks if program running + CALL NZ,L1601 ; routine CHAN-OPEN if so + CALL L0D4D ; routine TEMPS sets temporary colours. + CALL L1FDF ; routine PRINT-2 - the actual item + CALL L1BEE ; routine CHECK-END gives error if not at end + ; of statement + RET ; and return >>> + +; ------------------------------------ +; this subroutine is called from above +; and also from INPUT. + +;; PRINT-2 +L1FDF: RST 18H ; GET-CHAR gets printable character + CALL L2045 ; routine PR-END-Z checks if more printing + JR Z,L1FF2 ; to PRINT-4 if not e.g. just 'PRINT :' + +; This tight loop deals with combinations of positional controls and +; print items. An early return can be made from within the loop +; if the end of a print sequence is reached. + +;; PRINT-3 +L1FE5: CALL L204E ; routine PR-POSN-1 returns zero if more + ; but returns early at this point if + ; at end of statement! + ; + JR Z,L1FE5 ; to PRINT-3 if consecutive positioners + + CALL L1FFC ; routine PR-ITEM-1 deals with strings etc. + CALL L204E ; routine PR-POSN-1 for more position codes + JR Z,L1FE5 ; loop back to PRINT-3 if so + +;; PRINT-4 +L1FF2: CP $29 ; return now if this is ')' from input-item. + ; (see INPUT.) + RET Z ; or continue and print carriage return in + ; runtime + +; --------------------- +; Print carriage return +; --------------------- +; This routine which continues from above prints a carriage return +; in run-time. It is also called once from PRINT-POSN. + +;; PRINT-CR +L1FF5: CALL L1FC3 ; routine UNSTACK-Z + + LD A,$0D ; prepare a carriage return + + RST 10H ; PRINT-A + RET ; return + + +; ----------- +; Print items +; ----------- +; This routine deals with print items as in +; PRINT AT 10,0;"The value of A is ";a +; It returns once a single item has been dealt with as it is part +; of a tight loop that considers sequences of positional and print items + +;; PR-ITEM-1 +L1FFC: RST 18H ; GET-CHAR + CP $AC ; is character 'AT' ? + JR NZ,L200E ; forward to PR-ITEM-2 if not. + + CALL L1C79 ; routine NEXT-2NUM check for two comma + ; separated numbers placing them on the + ; calculator stack in runtime. + CALL L1FC3 ; routine UNSTACK-Z quits if checking syntax. + + CALL L2307 ; routine STK-TO-BC get the numbers in B and C. + LD A,$16 ; prepare the 'at' control. + JR L201E ; forward to PR-AT-TAB to print the sequence. + +; --- + +;; PR-ITEM-2 +L200E: CP $AD ; is character 'TAB' ? + JR NZ,L2024 ; to PR-ITEM-3 if not + + + RST 20H ; NEXT-CHAR to address next character + CALL L1C82 ; routine EXPT-1NUM + CALL L1FC3 ; routine UNSTACK-Z quits if checking syntax. + + CALL L1E99 ; routine FIND-INT2 puts integer in BC. + LD A,$17 ; prepare the 'tab' control. + +;; PR-AT-TAB +L201E: RST 10H ; PRINT-A outputs the control + + LD A,C ; first value to A + RST 10H ; PRINT-A outputs it. + + LD A,B ; second value + RST 10H ; PRINT-A + + RET ; return - item finished >>> + +; --- + +; Now consider paper 2; #2; a$ + +;; PR-ITEM-3 +L2024: CALL L21F2 ; routine CO-TEMP-3 will print any colour + RET NC ; items - return if success. + + CALL L2070 ; routine STR-ALTER considers new stream + RET NC ; return if altered. + + CALL L24FB ; routine SCANNING now to evaluate expression + CALL L1FC3 ; routine UNSTACK-Z if not runtime. + + BIT 6,(IY+$01) ; test FLAGS - Numeric or string result ? + CALL Z,L2BF1 ; routine STK-FETCH if string. + ; note no flags affected. + JP NZ,L2DE3 ; to PRINT-FP to print if numeric >>> + +; It was a string expression - start in DE, length in BC +; Now enter a loop to print it + +;; PR-STRING +L203C: LD A,B ; this tests if the + OR C ; length is zero and sets flag accordingly. + DEC BC ; this doesn't but decrements counter. + RET Z ; return if zero. + + LD A,(DE) ; fetch character. + INC DE ; address next location. + + RST 10H ; PRINT-A. + + JR L203C ; loop back to PR-STRING. + +; --------------- +; End of printing +; --------------- +; This subroutine returns zero if no further printing is required +; in the current statement. +; The first terminator is found in escaped input items only, +; the others in print_items. + +;; PR-END-Z +L2045: CP $29 ; is character a ')' ? + RET Z ; return if so - e.g. INPUT (p$); a$ + +;; PR-ST-END +L2048: CP $0D ; is it a carriage return ? + RET Z ; return also - e.g. PRINT a + + CP $3A ; is character a ':' ? + RET ; return - zero flag will be set if so. + ; e.g. PRINT a : + +; -------------- +; Print position +; -------------- +; This routine considers a single positional character ';', ',', ''' + +;; PR-POSN-1 +L204E: RST 18H ; GET-CHAR + CP $3B ; is it ';' ? + ; i.e. print from last position. + JR Z,L2067 ; forward to PR-POSN-3 if so. + ; i.e. do nothing. + + CP $2C ; is it ',' ? + ; i.e. print at next tabstop. + JR NZ,L2061 ; forward to PR-POSN-2 if anything else. + + CALL L2530 ; routine SYNTAX-Z + JR Z,L2067 ; forward to PR-POSN-3 if checking syntax. + + LD A,$06 ; prepare the 'comma' control character. + + RST 10H ; PRINT-A outputs to current channel in + ; run-time. + + JR L2067 ; skip to PR-POSN-3. + +; --- + +; check for newline. + +;; PR-POSN-2 +L2061: CP $27 ; is character a "'" ? (newline) + RET NZ ; return if no match >>> + + CALL L1FF5 ; routine PRINT-CR outputs a carriage return + ; in runtime only. + +;; PR-POSN-3 +L2067: RST 20H ; NEXT-CHAR to A. + CALL L2045 ; routine PR-END-Z checks if at end. + JR NZ,L206E ; to PR-POSN-4 if not. + + POP BC ; drop return address if at end. + +;; PR-POSN-4 +L206E: CP A ; reset the zero flag. + RET ; and return to loop or quit. + +; ------------ +; Alter stream +; ------------ +; This routine is called from PRINT ITEMS above, and also LIST as in +; LIST #15 + +;; STR-ALTER +L2070: CP $23 ; is character '#' ? + SCF ; set carry flag. + RET NZ ; return if no match. + + + RST 20H ; NEXT-CHAR + CALL L1C82 ; routine EXPT-1NUM gets stream number + AND A ; prepare to exit early with carry reset + CALL L1FC3 ; routine UNSTACK-Z exits early if parsing + CALL L1E94 ; routine FIND-INT1 gets number off stack + CP $10 ; must be range 0 - 15 decimal. + JP NC,L160E ; jump back to REPORT-Oa if not + ; 'Invalid stream'. + + CALL L1601 ; routine CHAN-OPEN + AND A ; clear carry - signal item dealt with. + RET ; return + +; -------------------- +; Handle INPUT command +; -------------------- +; This command +; + +;; INPUT +L2089: CALL L2530 ; routine SYNTAX-Z to check if in runtime. + JR Z,L2096 ; forward to INPUT-1 if checking syntax. + + LD A,$01 ; select channel 'K' the keyboard for input. + CALL L1601 ; routine CHAN-OPEN opens it. + CALL L0D6E ; routine CLS-LOWER clears the lower screen + ; and sets DF_SZ to two. + +;; INPUT-1 +L2096: LD (IY+$02),$01 ; update TV_FLAG - signal lower screen in use + ; ensuring that the correct set of system + ; variables are updated and that the border + ; colour is used. + + CALL L20C1 ; routine IN-ITEM-1 to handle the input. + + CALL L1BEE ; routine CHECK-END will make an early exit + ; if checking syntax. >>> + +; keyboard input has been made and it remains to adjust the upper +; screen in case the lower two lines have been extended upwards. + + LD BC,($5C88) ; fetch S_POSN current line/column of + ; the upper screen. + LD A,($5C6B) ; fetch DF_SZ the display file size of + ; the lower screen. + CP B ; test that lower screen does not overlap + JR C,L20AD ; forward to INPUT-2 if not. + +; the two screens overlap so adjust upper screen. + + LD C,$21 ; set column of upper screen to leftmost. + LD B,A ; and line to one above lower screen. + ; continue forward to update upper screen + ; print position. + +;; INPUT-2 +L20AD: LD ($5C88),BC ; set S_POSN update upper screen line/column. + LD A,$19 ; subtract from twenty five + SUB B ; the new line number. + LD ($5C8C),A ; and place result in SCR_CT - scroll count. + RES 0,(IY+$02) ; update TV_FLAG - signal main screen in use. + CALL L0DD9 ; routine CL-SET sets the print position + ; system variables for the upper screen. + JP L0D6E ; jump back to CLS-LOWER and make + ; an indirect exit >>. + +; --------------------- +; INPUT ITEM subroutine +; --------------------- +; This subroutine deals with the input items and print items. +; from the current input channel. +; It is only called from the above INPUT routine but was obviously +; once called from somewhere else in another context. + +;; IN-ITEM-1 +L20C1: CALL L204E ; routine PR-POSN-1 deals with a single + ; position item at each call. + JR Z,L20C1 ; back to IN-ITEM-1 until no more in a + ; sequence. + + CP $28 ; is character '(' ? + JR NZ,L20D8 ; forward to IN-ITEM-2 if not. + +; any variables within braces will be treated as part, or all, of the prompt +; instead of being used as destination variables. + + RST 20H ; NEXT-CHAR + CALL L1FDF ; routine PRINT-2 to output the dynamic + ; prompt. + + RST 18H ; GET-CHAR + CP $29 ; is character a matching ')' ? + JP NZ,L1C8A ; jump back to REPORT-C if not. + ; 'Nonsense in BASIC'. + + RST 20H ; NEXT-CHAR + JP L21B2 ; forward to IN-NEXT-2 + +; --- + +;; IN-ITEM-2 +L20D8: CP $CA ; is the character the token 'LINE' ? + JR NZ,L20ED ; forward to IN-ITEM-3 if not. + + RST 20H ; NEXT-CHAR - variable must come next. + CALL L1C1F ; routine CLASS-01 returns destination + ; address of variable to be assigned. + ; or generates an error if no variable + ; at this position. + + SET 7,(IY+$37) ; update FLAGX - signal handling INPUT LINE + BIT 6,(IY+$01) ; test FLAGS - numeric or string result ? + JP NZ,L1C8A ; jump back to REPORT-C if not string + ; 'Nonsense in BASIC'. + + JR L20FA ; forward to IN-PROMPT to set up workspace. + +; --- + +; the jump was here for other variables. + +;; IN-ITEM-3 +L20ED: CALL L2C8D ; routine ALPHA checks if character is + ; a suitable variable name. + JP NC,L21AF ; forward to IN-NEXT-1 if not + + CALL L1C1F ; routine CLASS-01 returns destination + ; address of variable to be assigned. + RES 7,(IY+$37) ; update FLAGX - signal not INPUT LINE. + +;; IN-PROMPT +L20FA: CALL L2530 ; routine SYNTAX-Z + JP Z,L21B2 ; forward to IN-NEXT-2 if checking syntax. + + CALL L16BF ; routine SET-WORK clears workspace. + LD HL,$5C71 ; point to system variable FLAGX + RES 6,(HL) ; signal string result. + SET 5,(HL) ; signal in Input Mode for editor. + LD BC,$0001 ; initialize space required to one for + ; the carriage return. + BIT 7,(HL) ; test FLAGX - INPUT LINE in use ? + JR NZ,L211C ; forward to IN-PR-2 if so as that is + ; all the space that is required. + + LD A,($5C3B) ; load accumulator from FLAGS + AND $40 ; mask to test BIT 6 of FLAGS and clear + ; the other bits in A. + ; numeric result expected ? + JR NZ,L211A ; forward to IN-PR-1 if so + + LD C,$03 ; increase space to three bytes for the + ; pair of surrounding quotes. + +;; IN-PR-1 +L211A: OR (HL) ; if numeric result, set bit 6 of FLAGX. + LD (HL),A ; and update system variable + +;; IN-PR-2 +L211C: RST 30H ; BC-SPACES opens 1 or 3 bytes in workspace + LD (HL),$0D ; insert carriage return at last new location. + LD A,C ; fetch the length, one or three. + RRCA ; lose bit 0. + RRCA ; test if quotes required. + JR NC,L2129 ; forward to IN-PR-3 if not. + + LD A,$22 ; load the '"' character + LD (DE),A ; place quote in first new location at DE. + DEC HL ; decrease HL - from carriage return. + LD (HL),A ; and place a quote in second location. + +;; IN-PR-3 +L2129: LD ($5C5B),HL ; set keyboard cursor K_CUR to HL + BIT 7,(IY+$37) ; test FLAGX - is this INPUT LINE ?? + JR NZ,L215E ; forward to IN-VAR-3 if so as input will + ; be accepted without checking its syntax. + + LD HL,($5C5D) ; fetch CH_ADD + PUSH HL ; and save on stack. + LD HL,($5C3D) ; fetch ERR_SP + PUSH HL ; and save on stack + +;; IN-VAR-1 +L213A: LD HL,L213A ; address: IN-VAR-1 - this address + PUSH HL ; is saved on stack to handle errors. + BIT 4,(IY+$30) ; test FLAGS2 - is K channel in use ? + JR Z,L2148 ; forward to IN-VAR-2 if not using the + ; keyboard for input. (??) + + LD ($5C3D),SP ; set ERR_SP to point to IN-VAR-1 on stack. + +;; IN-VAR-2 +L2148: LD HL,($5C61) ; set HL to WORKSP - start of workspace. + CALL L11A7 ; routine REMOVE-FP removes floating point + ; forms when looping in error condition. + LD (IY+$00),$FF ; set ERR_NR to 'OK' cancelling the error. + ; but X_PTR causes flashing error marker + ; to be displayed at each call to the editor. + CALL L0F2C ; routine EDITOR allows input to be entered + ; or corrected if this is second time around. + +; if we pass to next then there are no system errors + + RES 7,(IY+$01) ; update FLAGS - signal checking syntax + CALL L21B9 ; routine IN-ASSIGN checks syntax using + ; the VAL-FET-2 and powerful SCANNING routines. + ; any syntax error and its back to IN-VAR-1. + ; but with the flashing error marker showing + ; where the error is. + ; Note. the syntax of string input has to be + ; checked as the user may have removed the + ; bounding quotes or escaped them as with + ; "hat" + "stand" for example. +; proceed if syntax passed. + + JR L2161 ; jump forward to IN-VAR-4 + +; --- + +; the jump was to here when using INPUT LINE. + +;; IN-VAR-3 +L215E: CALL L0F2C ; routine EDITOR is called for input + +; when ENTER received rejoin other route but with no syntax check. + +; INPUT and INPUT LINE converge here. + +;; IN-VAR-4 +L2161: LD (IY+$22),$00 ; set K_CUR_hi to a low value so that the cursor + ; no longer appears in the input line. + + CALL L21D6 ; routine IN-CHAN-K tests if the keyboard + ; is being used for input. + JR NZ,L2174 ; forward to IN-VAR-5 if using another input + ; channel. + +; continue here if using the keyboard. + + CALL L111D ; routine ED-COPY overprints the edit line + ; to the lower screen. The only visible + ; affect is that the cursor disappears. + ; if you're inputting more than one item in + ; a statement then that becomes apparent. + + LD BC,($5C82) ; fetch line and column from ECHO_E + CALL L0DD9 ; routine CL-SET sets S-POSNL to those + ; values. + +; if using another input channel rejoin here. + +;; IN-VAR-5 +L2174: LD HL,$5C71 ; point HL to FLAGX + RES 5,(HL) ; signal not in input mode + BIT 7,(HL) ; is this INPUT LINE ? + RES 7,(HL) ; cancel the bit anyway. + JR NZ,L219B ; forward to IN-VAR-6 if INPUT LINE. + + POP HL ; drop the looping address + POP HL ; drop the the address of previous + ; error handler. + LD ($5C3D),HL ; set ERR_SP to point to it. + POP HL ; drop original CH_ADD which points to + ; INPUT command in BASIC line. + LD ($5C5F),HL ; save in X_PTR while input is assigned. + SET 7,(IY+$01) ; update FLAGS - Signal running program + CALL L21B9 ; routine IN-ASSIGN is called again + ; this time the variable will be assigned + ; the input value without error. + ; Note. the previous example now + ; becomes "hatstand" + + LD HL,($5C5F) ; fetch stored CH_ADD value from X_PTR. + LD (IY+$26),$00 ; set X_PTR_hi so that iy is no longer relevant. + LD ($5C5D),HL ; put restored value back in CH_ADD + JR L21B2 ; forward to IN-NEXT-2 to see if anything + ; more in the INPUT list. + +; --- + +; the jump was to here with INPUT LINE only + +;; IN-VAR-6 +L219B: LD HL,($5C63) ; STKBOT points to the end of the input. + LD DE,($5C61) ; WORKSP points to the beginning. + SCF ; prepare for true subtraction. + SBC HL,DE ; subtract to get length + LD B,H ; transfer it to + LD C,L ; the BC register pair. + CALL L2AB2 ; routine STK-STO-$ stores parameters on + ; the calculator stack. + CALL L2AFF ; routine LET assigns it to destination. + JR L21B2 ; forward to IN-NEXT-2 as print items + ; not allowed with INPUT LINE. + ; Note. that "hat" + "stand" will, for + ; example, be unchanged as also would + ; 'PRINT "Iris was here"'. + +; --- + +; the jump was to here when ALPHA found more items while looking for +; a variable name. + +;; IN-NEXT-1 +L21AF: CALL L1FFC ; routine PR-ITEM-1 considers further items. + +;; IN-NEXT-2 +L21B2: CALL L204E ; routine PR-POSN-1 handles a position item. + JP Z,L20C1 ; jump back to IN-ITEM-1 if the zero flag + ; indicates more items are present. + + RET ; return. + +; --------------------------- +; INPUT ASSIGNMENT Subroutine +; --------------------------- +; This subroutine is called twice from the INPUT command when normal +; keyboard input is assigned. On the first occasion syntax is checked +; using SCANNING. The final call with the syntax flag reset is to make +; the assignment. + +;; IN-ASSIGN +L21B9: LD HL,($5C61) ; fetch WORKSP start of input + LD ($5C5D),HL ; set CH_ADD to first character + + RST 18H ; GET-CHAR ignoring leading white-space. + CP $E2 ; is it 'STOP' + JR Z,L21D0 ; forward to IN-STOP if so. + + LD A,($5C71) ; load accumulator from FLAGX + CALL L1C59 ; routine VAL-FET-2 makes assignment + ; or goes through the motions if checking + ; syntax. SCANNING is used. + + RST 18H ; GET-CHAR + CP $0D ; is it carriage return ? + RET Z ; return if so + ; either syntax is OK + ; or assignment has been made. + +; if another character was found then raise an error. +; User doesn't see report but the flashing error marker +; appears in the lower screen. + +;; REPORT-Cb +L21CE: RST 08H ; ERROR-1 + DEFB $0B ; Error Report: Nonsense in BASIC + +;; IN-STOP +L21D0: CALL L2530 ; routine SYNTAX-Z (UNSTACK-Z?) + RET Z ; return if checking syntax + ; as user wouldn't see error report. + ; but generate visible error report + ; on second invocation. + +;; REPORT-H +L21D4: RST 08H ; ERROR-1 + DEFB $10 ; Error Report: STOP in INPUT + +; ------------------ +; Test for channel K +; ------------------ +; This subroutine is called once from the keyboard +; INPUT command to check if the input routine in +; use is the one for the keyboard. + +;; IN-CHAN-K +L21D6: LD HL,($5C51) ; fetch address of current channel CURCHL + INC HL ; + INC HL ; advance past + INC HL ; input and + INC HL ; output streams + LD A,(HL) ; fetch the channel identifier. + CP $4B ; test for 'K' + RET ; return with zero set if keyboard is use. + +; -------------------- +; Colour Item Routines +; -------------------- +; +; These routines have 3 entry points - +; 1) CO-TEMP-2 to handle a series of embedded Graphic colour items. +; 2) CO-TEMP-3 to handle a single embedded print colour item. +; 3) CO TEMP-4 to handle a colour command such as FLASH 1 +; +; "Due to a bug, if you bring in a peripheral channel and later use a colour +; statement, colour controls will be sent to it by mistake." - Steven Vickers +; Pitman Pocket Guide, 1984. +; +; To be fair, this only applies if the last channel was other than 'K', 'S' +; or 'P', which are all that are supported by this ROM, but if that last +; channel was a microdrive file, network channel etc. then +; PAPER 6; CLS will not turn the screen yellow and +; CIRCLE INK 2; 128,88,50 will not draw a red circle. +; +; This bug does not apply to embedded PRINT items as it is quite permissible +; to mix stream altering commands and colour items. +; The fix therefore would be to ensure that CLASS-07 and CLASS-09 make +; channel 'S' the current channel when not checking syntax. +; ----------------------------------------------------------------- + +;; CO-TEMP-1 +L21E1: RST 20H ; NEXT-CHAR + +; -> Entry point from CLASS-09. Embedded Graphic colour items. +; e.g. PLOT INK 2; PAPER 8; 128,88 +; Loops till all colour items output, finally addressing the coordinates. + +;; CO-TEMP-2 +L21E2: CALL L21F2 ; routine CO-TEMP-3 to output colour control. + RET C ; return if nothing more to output. -> + + + RST 18H ; GET-CHAR + CP $2C ; is it ',' separator ? + JR Z,L21E1 ; back if so to CO-TEMP-1 + + CP $3B ; is it ';' separator ? + JR Z,L21E1 ; back to CO-TEMP-1 for more. + + JP L1C8A ; to REPORT-C (REPORT-Cb is within range) + ; 'Nonsense in BASIC' + +; ------------------- +; CO-TEMP-3 +; ------------------- +; -> this routine evaluates and outputs a colour control and parameter. +; It is called from above and also from PR-ITEM-3 to handle a single embedded +; print item e.g. PRINT PAPER 6; "Hi". In the latter case, the looping for +; multiple items is within the PR-ITEM routine. +; It is quite permissible to send these to any stream. + +;; CO-TEMP-3 +L21F2: CP $D9 ; is it 'INK' ? + RET C ; return if less. + + CP $DF ; compare with 'OUT' + CCF ; Complement Carry Flag + RET C ; return if greater than 'OVER', $DE. + + PUSH AF ; save the colour token. + + RST 20H ; address NEXT-CHAR + POP AF ; restore token and continue. + +; -> this entry point used by CLASS-07. e.g. the command PAPER 6. + +;; CO-TEMP-4 +L21FC: SUB $C9 ; reduce to control character $10 (INK) + ; thru $15 (OVER). + PUSH AF ; save control. + CALL L1C82 ; routine EXPT-1NUM stacks addressed + ; parameter on calculator stack. + POP AF ; restore control. + AND A ; clear carry + + CALL L1FC3 ; routine UNSTACK-Z returns if checking syntax. + + PUSH AF ; save again + CALL L1E94 ; routine FIND-INT1 fetches parameter to A. + LD D,A ; transfer now to D + POP AF ; restore control. + + RST 10H ; PRINT-A outputs the control to current + ; channel. + LD A,D ; transfer parameter to A. + + RST 10H ; PRINT-A outputs parameter. + RET ; return. -> + +; ------------------------------------------------------------------------- +; +; {fl}{br}{ paper }{ ink } The temporary colour attributes +; ___ ___ ___ ___ ___ ___ ___ ___ system variable. +; ATTR_T | | | | | | | | | +; | | | | | | | | | +; 23695 |___|___|___|___|___|___|___|___| +; 7 6 5 4 3 2 1 0 +; +; +; {fl}{br}{ paper }{ ink } The temporary mask used for +; ___ ___ ___ ___ ___ ___ ___ ___ transparent colours. Any bit +; MASK_T | | | | | | | | | that is 1 shows that the +; | | | | | | | | | corresponding attribute is +; 23696 |___|___|___|___|___|___|___|___| taken not from ATTR-T but from +; 7 6 5 4 3 2 1 0 what is already on the screen. +; +; +; {paper9 }{ ink9 }{ inv1 }{ over1} The print flags. Even bits are +; ___ ___ ___ ___ ___ ___ ___ ___ temporary flags. The odd bits +; P_FLAG | | | | | | | | | are the permanent flags. +; | p | t | p | t | p | t | p | t | +; 23697 |___|___|___|___|___|___|___|___| +; 7 6 5 4 3 2 1 0 +; +; ----------------------------------------------------------------------- + +; ------------------------------------ +; The colour system variable handler. +; ------------------------------------ +; This is an exit branch from PO-1-OPER, PO-2-OPER +; A holds control $10 (INK) to $15 (OVER) +; D holds parameter 0-9 for ink/paper 0,1 or 8 for bright/flash, +; 0 or 1 for over/inverse. + +;; CO-TEMP-5 +L2211: SUB $11 ; reduce range $FF-$04 + ADC A,$00 ; add in carry if INK + JR Z,L2234 ; forward to CO-TEMP-7 with INK and PAPER. + + SUB $02 ; reduce range $FF-$02 + ADC A,$00 ; add carry if FLASH + JR Z,L2273 ; forward to CO-TEMP-C with FLASH and BRIGHT. + + CP $01 ; is it 'INVERSE' ? + LD A,D ; fetch parameter for INVERSE/OVER + LD B,$01 ; prepare OVER mask setting bit 0. + JR NZ,L2228 ; forward to CO-TEMP-6 if OVER + + RLCA ; shift bit 0 + RLCA ; to bit 2 + LD B,$04 ; set bit 2 of mask for inverse. + +;; CO-TEMP-6 +L2228: LD C,A ; save the A + LD A,D ; re-fetch parameter + CP $02 ; is it less than 2 + JR NC,L2244 ; to REPORT-K if not 0 or 1. + ; 'Invalid colour'. + + LD A,C ; restore A + LD HL,$5C91 ; address system variable P_FLAG + JR L226C ; forward to exit via routine CO-CHANGE + +; --- + +; the branch was here with INK/PAPER and carry set for INK. + +;; CO-TEMP-7 +L2234: LD A,D ; fetch parameter + LD B,$07 ; set ink mask 00000111 + JR C,L223E ; forward to CO-TEMP-8 with INK + + RLCA ; shift bits 0-2 + RLCA ; to + RLCA ; bits 3-5 + LD B,$38 ; set paper mask 00111000 + +; both paper and ink rejoin here + +;; CO-TEMP-8 +L223E: LD C,A ; value to C + LD A,D ; fetch parameter + CP $0A ; is it less than 10d ? + JR C,L2246 ; forward to CO-TEMP-9 if so. + +; ink 10 etc. is not allowed. + +;; REPORT-K +L2244: RST 08H ; ERROR-1 + DEFB $13 ; Error Report: Invalid colour + +;; CO-TEMP-9 +L2246: LD HL,$5C8F ; address system variable ATTR_T initially. + CP $08 ; compare with 8 + JR C,L2258 ; forward to CO-TEMP-B with 0-7. + + LD A,(HL) ; fetch temporary attribute as no change. + JR Z,L2257 ; forward to CO-TEMP-A with INK/PAPER 8 + +; it is either ink 9 or paper 9 (contrasting) + + OR B ; or with mask to make white + CPL ; make black and change other to dark + AND $24 ; 00100100 + JR Z,L2257 ; forward to CO-TEMP-A if black and + ; originally light. + + LD A,B ; else just use the mask (white) + +;; CO-TEMP-A +L2257: LD C,A ; save A in C + +;; CO-TEMP-B +L2258: LD A,C ; load colour to A + CALL L226C ; routine CO-CHANGE addressing ATTR-T + + LD A,$07 ; put 7 in accumulator + CP D ; compare with parameter + SBC A,A ; $00 if 0-7, $FF if 8 + CALL L226C ; routine CO-CHANGE addressing MASK-T + ; mask returned in A. + +; now consider P-FLAG. + + RLCA ; 01110000 or 00001110 + RLCA ; 11100000 or 00011100 + AND $50 ; 01000000 or 00010000 (AND 01010000) + LD B,A ; transfer to mask + LD A,$08 ; load A with 8 + CP D ; compare with parameter + SBC A,A ; $FF if was 9, $00 if 0-8 + ; continue while addressing P-FLAG + ; setting bit 4 if ink 9 + ; setting bit 6 if paper 9 + +; ----------------------- +; Handle change of colour +; ----------------------- +; This routine addresses a system variable ATTR_T, MASK_T or P-FLAG in HL. +; colour value in A, mask in B. + +;; CO-CHANGE +L226C: XOR (HL) ; impress bits specified + AND B ; by mask + XOR (HL) ; on system variable. + LD (HL),A ; update system variable. + INC HL ; address next location. + LD A,B ; put current value of mask in A + RET ; return. + +; --- + +; the branch was here with flash and bright + +;; CO-TEMP-C +L2273: SBC A,A ; set zero flag for bright. + LD A,D ; fetch original parameter 0,1 or 8 + RRCA ; rotate bit 0 to bit 7 + LD B,$80 ; mask for flash 10000000 + JR NZ,L227D ; forward to CO-TEMP-D if flash + + RRCA ; rotate bit 7 to bit 6 + LD B,$40 ; mask for bright 01000000 + +;; CO-TEMP-D +L227D: LD C,A ; store value in C + LD A,D ; fetch parameter + CP $08 ; compare with 8 + JR Z,L2287 ; forward to CO-TEMP-E if 8 + + CP $02 ; test if 0 or 1 + JR NC,L2244 ; back to REPORT-K if not + ; 'Invalid colour' + +;; CO-TEMP-E +L2287: LD A,C ; value to A + LD HL,$5C8F ; address ATTR_T + CALL L226C ; routine CO-CHANGE addressing ATTR_T + LD A,C ; fetch value + RRCA ; for flash8/bright8 complete + RRCA ; rotations to put set bit in + RRCA ; bit 7 (flash) bit 6 (bright) + JR L226C ; back to CO-CHANGE addressing MASK_T + ; and indirect return. + +; --------------------- +; Handle BORDER command +; --------------------- +; Command syntax example: BORDER 7 +; This command routine sets the border to one of the eight colours. +; The colours used for the lower screen are based on this. + +;; BORDER +L2294: CALL L1E94 ; routine FIND-INT1 + CP $08 ; must be in range 0 (black) to 7 (white) + JR NC,L2244 ; back to REPORT-K if not + ; 'Invalid colour'. + + OUT ($FE),A ; outputting to port effects an immediate + ; change. + RLCA ; shift the colour to + RLCA ; the paper bits setting the + RLCA ; ink colour black. + BIT 5,A ; is the number light coloured ? + ; i.e. in the range green to white. + JR NZ,L22A6 ; skip to BORDER-1 if so + + XOR $07 ; make the ink white. + +;; BORDER-1 +L22A6: LD ($5C48),A ; update BORDCR with new paper/ink + RET ; return. + +; ----------------- +; Get pixel address +; ----------------- +; +; + +;; PIXEL-ADD +L22AA: LD A,$AF ; load with 175 decimal. + SUB B ; subtract the y value. + JP C,L24F9 ; jump forward to REPORT-Bc if greater. + ; 'Integer out of range' + +; the high byte is derived from Y only. +; the first 3 bits are always 010 +; the next 2 bits denote in which third of the screen the byte is. +; the last 3 bits denote in which of the 8 scan lines within a third +; the byte is located. There are 24 discrete values. + + + LD B,A ; the line number from top of screen to B. + AND A ; clear carry (already clear) + RRA ; 0xxxxxxx + SCF ; set carry flag + RRA ; 10xxxxxx + AND A ; clear carry flag + RRA ; 010xxxxx + + XOR B ; + AND $F8 ; keep the top 5 bits 11111000 + XOR B ; 010xxbbb + LD H,A ; transfer high byte to H. + +; the low byte is derived from both X and Y. + + LD A,C ; the x value 0-255. + RLCA ; + RLCA ; + RLCA ; + XOR B ; the y value + AND $C7 ; apply mask 11000111 + XOR B ; restore unmasked bits xxyyyxxx + RLCA ; rotate to xyyyxxxx + RLCA ; required position. yyyxxxxx + LD L,A ; low byte to L. + +; finally form the pixel position in A. + + LD A,C ; x value to A + AND $07 ; mod 8 + RET ; return + +; ---------------- +; Point Subroutine +; ---------------- +; The point subroutine is called from s-point via the scanning functions +; table. + +;; POINT-SUB +L22CB: CALL L2307 ; routine STK-TO-BC + CALL L22AA ; routine PIXEL-ADD finds address of pixel. + LD B,A ; pixel position to B, 0-7. + INC B ; increment to give rotation count 1-8. + LD A,(HL) ; fetch byte from screen. + +;; POINT-LP +L22D4: RLCA ; rotate and loop back + DJNZ L22D4 ; to POINT-LP until pixel at right. + + AND $01 ; test to give zero or one. + JP L2D28 ; jump forward to STACK-A to save result. + +; ------------------- +; Handle PLOT command +; ------------------- +; Command Syntax example: PLOT 128,88 +; + +;; PLOT +L22DC: CALL L2307 ; routine STK-TO-BC + CALL L22E5 ; routine PLOT-SUB + JP L0D4D ; to TEMPS + +; ------------------- +; The Plot subroutine +; ------------------- +; A screen byte holds 8 pixels so it is necessary to rotate a mask +; into the correct position to leave the other 7 pixels unaffected. +; However all 64 pixels in the character cell take any embedded colour +; items. +; A pixel can be reset (inverse 1), toggled (over 1), or set ( with inverse +; and over switches off). With both switches on, the byte is simply put +; back on the screen though the colours may change. + +;; PLOT-SUB +L22E5: LD ($5C7D),BC ; store new x/y values in COORDS + CALL L22AA ; routine PIXEL-ADD gets address in HL, + ; count from left 0-7 in B. + LD B,A ; transfer count to B. + INC B ; increase 1-8. + LD A,$FE ; 11111110 in A. + +;; PLOT-LOOP +L22F0: RRCA ; rotate mask. + DJNZ L22F0 ; to PLOT-LOOP until B circular rotations. + + LD B,A ; load mask to B + LD A,(HL) ; fetch screen byte to A + + LD C,(IY+$57) ; P_FLAG to C + BIT 0,C ; is it to be OVER 1 ? + JR NZ,L22FD ; forward to PL-TST-IN if so. + +; was over 0 + + AND B ; combine with mask to blank pixel. + +;; PL-TST-IN +L22FD: BIT 2,C ; is it inverse 1 ? + JR NZ,L2303 ; to PLOT-END if so. + + XOR B ; switch the pixel + CPL ; restore other 7 bits + +;; PLOT-END +L2303: LD (HL),A ; load byte to the screen. + JP L0BDB ; exit to PO-ATTR to set colours for cell. + +; ------------------------------ +; Put two numbers in BC register +; ------------------------------ +; +; + +;; STK-TO-BC +L2307: CALL L2314 ; routine STK-TO-A + LD B,A ; + PUSH BC ; + CALL L2314 ; routine STK-TO-A + LD E,C ; + POP BC ; + LD D,C ; + LD C,A ; + RET ; + +; ----------------------- +; Put stack in A register +; ----------------------- +; This routine puts the last value on the calculator stack into the accumulator +; deleting the last value. + +;; STK-TO-A +L2314: CALL L2DD5 ; routine FP-TO-A compresses last value into + ; accumulator. e.g. PI would become 3. + ; zero flag set if positive. + JP C,L24F9 ; jump forward to REPORT-Bc if >= 255.5. + + LD C,$01 ; prepare a positive sign byte. + RET Z ; return if FP-TO-BC indicated positive. + + LD C,$FF ; prepare negative sign byte and + RET ; return. + + +; --------------------- +; Handle CIRCLE command +; --------------------- +; +; syntax has been partly checked using the class for draw command. + +;; CIRCLE +L2320: RST 18H ; GET-CHAR + CP $2C ; is it required comma ? + JP NZ,L1C8A ; jump to REPORT-C if not + + + RST 20H ; NEXT-CHAR + CALL L1C82 ; routine EXPT-1NUM fetches radius + CALL L1BEE ; routine CHECK-END will return here if + ; nothing follows command. + + RST 28H ;; FP-CALC + DEFB $2A ;;abs ; make radius positive + DEFB $3D ;;re-stack ; in full floating point form + DEFB $38 ;;end-calc + + LD A,(HL) ; fetch first floating point byte + CP $81 ; compare to one + JR NC,L233B ; forward to C-R-GRE-1 if circle radius + ; is greater than one. + + + RST 28H ;; FP-CALC + DEFB $02 ;;delete ; delete the radius from stack. + DEFB $38 ;;end-calc + + JR L22DC ; to PLOT to just plot x,y. + +; --- + + +;; C-R-GRE-1 +L233B: RST 28H ;; FP-CALC ; x, y, r + DEFB $A3 ;;stk-pi/2 ; x, y, r, pi/2. + DEFB $38 ;;end-calc + + LD (HL),$83 ; ; x, y, r, 2*PI + + RST 28H ;; FP-CALC + DEFB $C5 ;;st-mem-5 ; store 2*PI in mem-5 + DEFB $02 ;;delete ; x, y, z. + DEFB $38 ;;end-calc + + CALL L247D ; routine CD-PRMS1 + PUSH BC ; + + RST 28H ;; FP-CALC + DEFB $31 ;;duplicate + DEFB $E1 ;;get-mem-1 + DEFB $04 ;;multiply + DEFB $38 ;;end-calc + + LD A,(HL) ; + CP $80 ; + JR NC,L235A ; to C-ARC-GE1 + + + RST 28H ;; FP-CALC + DEFB $02 ;;delete + DEFB $02 ;;delete + DEFB $38 ;;end-calc + + POP BC ; + JP L22DC ; JUMP to PLOT + +; --- + + +;; C-ARC-GE1 +L235A: RST 28H ;; FP-CALC + DEFB $C2 ;;st-mem-2 + DEFB $01 ;;exchange + DEFB $C0 ;;st-mem-0 + DEFB $02 ;;delete + DEFB $03 ;;subtract + DEFB $01 ;;exchange + DEFB $E0 ;;get-mem-0 + DEFB $0F ;;addition + DEFB $C0 ;;st-mem-0 + DEFB $01 ;;exchange + DEFB $31 ;;duplicate + DEFB $E0 ;;get-mem-0 + DEFB $01 ;;exchange + DEFB $31 ;;duplicate + DEFB $E0 ;;get-mem-0 + DEFB $A0 ;;stk-zero + DEFB $C1 ;;st-mem-1 + DEFB $02 ;;delete + DEFB $38 ;;end-calc + + INC (IY+$62) ; MEM-2-1st + CALL L1E94 ; routine FIND-INT1 + LD L,A ; + PUSH HL ; + CALL L1E94 ; routine FIND-INT1 + POP HL ; + LD H,A ; + LD ($5C7D),HL ; COORDS + POP BC ; + JP L2420 ; to DRW-STEPS + + +; ------------------- +; Handle DRAW command +; ------------------- +; +; + +;; DRAW +L2382: RST 18H ; GET-CHAR + CP $2C ; + JR Z,L238D ; to DR-3-PRMS + + CALL L1BEE ; routine CHECK-END + JP L2477 ; to LINE-DRAW + +; --- + +;; DR-3-PRMS +L238D: RST 20H ; NEXT-CHAR + CALL L1C82 ; routine EXPT-1NUM + CALL L1BEE ; routine CHECK-END + + RST 28H ;; FP-CALC + DEFB $C5 ;;st-mem-5 + DEFB $A2 ;;stk-half + DEFB $04 ;;multiply + DEFB $1F ;;sin + DEFB $31 ;;duplicate + DEFB $30 ;;not + DEFB $30 ;;not + DEFB $00 ;;jump-true + + DEFB $06 ;;to L23A3, DR-SIN-NZ + + DEFB $02 ;;delete + DEFB $38 ;;end-calc + + JP L2477 ; to LINE-DRAW + +; --- + +;; DR-SIN-NZ +L23A3: DEFB $C0 ;;st-mem-0 + DEFB $02 ;;delete + DEFB $C1 ;;st-mem-1 + DEFB $02 ;;delete + DEFB $31 ;;duplicate + DEFB $2A ;;abs + DEFB $E1 ;;get-mem-1 + DEFB $01 ;;exchange + DEFB $E1 ;;get-mem-1 + DEFB $2A ;;abs + DEFB $0F ;;addition + DEFB $E0 ;;get-mem-0 + DEFB $05 ;;division + DEFB $2A ;;abs + DEFB $E0 ;;get-mem-0 + DEFB $01 ;;exchange + DEFB $3D ;;re-stack + DEFB $38 ;;end-calc + + LD A,(HL) ; + CP $81 ; + JR NC,L23C1 ; to DR-PRMS + + + RST 28H ;; FP-CALC + DEFB $02 ;;delete + DEFB $02 ;;delete + DEFB $38 ;;end-calc + + JP L2477 ; to LINE-DRAW + +; --- + +;; DR-PRMS +L23C1: CALL L247D ; routine CD-PRMS1 + PUSH BC ; + + RST 28H ;; FP-CALC + DEFB $02 ;;delete + DEFB $E1 ;;get-mem-1 + DEFB $01 ;;exchange + DEFB $05 ;;division + DEFB $C1 ;;st-mem-1 + DEFB $02 ;;delete + DEFB $01 ;;exchange + DEFB $31 ;;duplicate + DEFB $E1 ;;get-mem-1 + DEFB $04 ;;multiply + DEFB $C2 ;;st-mem-2 + DEFB $02 ;;delete + DEFB $01 ;;exchange + DEFB $31 ;;duplicate + DEFB $E1 ;;get-mem-1 + DEFB $04 ;;multiply + DEFB $E2 ;;get-mem-2 + DEFB $E5 ;;get-mem-5 + DEFB $E0 ;;get-mem-0 + DEFB $03 ;;subtract + DEFB $A2 ;;stk-half + DEFB $04 ;;multiply + DEFB $31 ;;duplicate + DEFB $1F ;;sin + DEFB $C5 ;;st-mem-5 + DEFB $02 ;;delete + DEFB $20 ;;cos + DEFB $C0 ;;st-mem-0 + DEFB $02 ;;delete + DEFB $C2 ;;st-mem-2 + DEFB $02 ;;delete + DEFB $C1 ;;st-mem-1 + DEFB $E5 ;;get-mem-5 + DEFB $04 ;;multiply + DEFB $E0 ;;get-mem-0 + DEFB $E2 ;;get-mem-2 + DEFB $04 ;;multiply + DEFB $0F ;;addition + DEFB $E1 ;;get-mem-1 + DEFB $01 ;;exchange + DEFB $C1 ;;st-mem-1 + DEFB $02 ;;delete + DEFB $E0 ;;get-mem-0 + DEFB $04 ;;multiply + DEFB $E2 ;;get-mem-2 + DEFB $E5 ;;get-mem-5 + DEFB $04 ;;multiply + DEFB $03 ;;subtract + DEFB $C2 ;;st-mem-2 + DEFB $2A ;;abs + DEFB $E1 ;;get-mem-1 + DEFB $2A ;;abs + DEFB $0F ;;addition + DEFB $02 ;;delete + DEFB $38 ;;end-calc + + LD A,(DE) ; + CP $81 ; + POP BC ; + JP C,L2477 ; to LINE-DRAW + + PUSH BC ; + + RST 28H ;; FP-CALC + DEFB $01 ;;exchange + DEFB $38 ;;end-calc + + LD A,($5C7D) ; COORDS-x + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $C0 ;;st-mem-0 + DEFB $0F ;;addition + DEFB $01 ;;exchange + DEFB $38 ;;end-calc + + LD A,($5C7E) ; COORDS-y + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $C5 ;;st-mem-5 + DEFB $0F ;;addition + DEFB $E0 ;;get-mem-0 + DEFB $E5 ;;get-mem-5 + DEFB $38 ;;end-calc + + POP BC ; + +;; DRW-STEPS +L2420: DEC B ; + JR Z,L245F ; to ARC-END + + JR L2439 ; to ARC-START + +; --- + + +;; ARC-LOOP +L2425: RST 28H ;; FP-CALC + DEFB $E1 ;;get-mem-1 + DEFB $31 ;;duplicate + DEFB $E3 ;;get-mem-3 + DEFB $04 ;;multiply + DEFB $E2 ;;get-mem-2 + DEFB $E4 ;;get-mem-4 + DEFB $04 ;;multiply + DEFB $03 ;;subtract + DEFB $C1 ;;st-mem-1 + DEFB $02 ;;delete + DEFB $E4 ;;get-mem-4 + DEFB $04 ;;multiply + DEFB $E2 ;;get-mem-2 + DEFB $E3 ;;get-mem-3 + DEFB $04 ;;multiply + DEFB $0F ;;addition + DEFB $C2 ;;st-mem-2 + DEFB $02 ;;delete + DEFB $38 ;;end-calc + +;; ARC-START +L2439: PUSH BC ; + + RST 28H ;; FP-CALC + DEFB $C0 ;;st-mem-0 + DEFB $02 ;;delete + DEFB $E1 ;;get-mem-1 + DEFB $0F ;;addition + DEFB $31 ;;duplicate + DEFB $38 ;;end-calc + + LD A,($5C7D) ; COORDS-x + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $03 ;;subtract + DEFB $E0 ;;get-mem-0 + DEFB $E2 ;;get-mem-2 + DEFB $0F ;;addition + DEFB $C0 ;;st-mem-0 + DEFB $01 ;;exchange + DEFB $E0 ;;get-mem-0 + DEFB $38 ;;end-calc + + LD A,($5C7E) ; COORDS-y + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $03 ;;subtract + DEFB $38 ;;end-calc + + CALL L24B7 ; routine DRAW-LINE + POP BC ; + DJNZ L2425 ; to ARC-LOOP + + +;; ARC-END +L245F: RST 28H ;; FP-CALC + DEFB $02 ;;delete + DEFB $02 ;;delete + DEFB $01 ;;exchange + DEFB $38 ;;end-calc + + LD A,($5C7D) ; COORDS-x + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $03 ;;subtract + DEFB $01 ;;exchange + DEFB $38 ;;end-calc + + LD A,($5C7E) ; COORDS-y + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $03 ;;subtract + DEFB $38 ;;end-calc + +;; LINE-DRAW +L2477: CALL L24B7 ; routine DRAW-LINE + JP L0D4D ; to TEMPS + + +; ------------------ +; Initial parameters +; ------------------ +; +; + +;; CD-PRMS1 +L247D: RST 28H ;; FP-CALC + DEFB $31 ;;duplicate + DEFB $28 ;;sqr + DEFB $34 ;;stk-data + DEFB $32 ;;Exponent: $82, Bytes: 1 + DEFB $00 ;;(+00,+00,+00) + DEFB $01 ;;exchange + DEFB $05 ;;division + DEFB $E5 ;;get-mem-5 + DEFB $01 ;;exchange + DEFB $05 ;;division + DEFB $2A ;;abs + DEFB $38 ;;end-calc + + CALL L2DD5 ; routine FP-TO-A + JR C,L2495 ; to USE-252 + + AND $FC ; + ADD A,$04 ; + JR NC,L2497 ; to DRAW-SAVE + +;; USE-252 +L2495: LD A,$FC ; + +;; DRAW-SAVE +L2497: PUSH AF ; + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $E5 ;;get-mem-5 + DEFB $01 ;;exchange + DEFB $05 ;;division + DEFB $31 ;;duplicate + DEFB $1F ;;sin + DEFB $C4 ;;st-mem-4 + DEFB $02 ;;delete + DEFB $31 ;;duplicate + DEFB $A2 ;;stk-half + DEFB $04 ;;multiply + DEFB $1F ;;sin + DEFB $C1 ;;st-mem-1 + DEFB $01 ;;exchange + DEFB $C0 ;;st-mem-0 + DEFB $02 ;;delete + DEFB $31 ;;duplicate + DEFB $04 ;;multiply + DEFB $31 ;;duplicate + DEFB $0F ;;addition + DEFB $A1 ;;stk-one + DEFB $03 ;;subtract + DEFB $1B ;;negate + DEFB $C3 ;;st-mem-3 + DEFB $02 ;;delete + DEFB $38 ;;end-calc + + POP BC ; + RET ; + +; ------------ +; Line drawing +; ------------ +; +; + +;; DRAW-LINE +L24B7: CALL L2307 ; routine STK-TO-BC + LD A,C ; + CP B ; + JR NC,L24C4 ; to DL-X-GE-Y + + LD L,C ; + PUSH DE ; + XOR A ; + LD E,A ; + JR L24CB ; to DL-LARGER + +; --- + +;; DL-X-GE-Y +L24C4: OR C ; + RET Z ; + + LD L,B ; + LD B,C ; + PUSH DE ; + LD D,$00 ; + +;; DL-LARGER +L24CB: LD H,B ; + LD A,B ; + RRA ; + +;; D-L-LOOP +L24CE: ADD A,L ; + JR C,L24D4 ; to D-L-DIAG + + CP H ; + JR C,L24DB ; to D-L-HR-VT + +;; D-L-DIAG +L24D4: SUB H ; + LD C,A ; + EXX ; + POP BC ; + PUSH BC ; + JR L24DF ; to D-L-STEP + +; --- + +;; D-L-HR-VT +L24DB: LD C,A ; + PUSH DE ; + EXX ; + POP BC ; + +;; D-L-STEP +L24DF: LD HL,($5C7D) ; COORDS + LD A,B ; + ADD A,H ; + LD B,A ; + LD A,C ; + INC A ; + ADD A,L ; + JR C,L24F7 ; to D-L-RANGE + + JR Z,L24F9 ; to REPORT-Bc + +;; D-L-PLOT +L24EC: DEC A ; + LD C,A ; + CALL L22E5 ; routine PLOT-SUB + EXX ; + LD A,C ; + DJNZ L24CE ; to D-L-LOOP + + POP DE ; + RET ; + +; --- + +;; D-L-RANGE +L24F7: JR Z,L24EC ; to D-L-PLOT + + +;; REPORT-Bc +L24F9: RST 08H ; ERROR-1 + DEFB $0A ; Error Report: Integer out of range + + + +;*********************************** +;** Part 8. EXPRESSION EVALUATION ** +;*********************************** +; +; It is a this stage of the ROM that the Spectrum ceases altogether to be +; just a colourful novelty. One remarkable feature is that in all previous +; commands when the Spectrum is expecting a number or a string then an +; expression of the same type can be substituted ad infinitum. +; This is the routine that evaluates that expression. +; This is what causes 2 + 2 to give the answer 4. +; That is quite easy to understand. However you don't have to make it much +; more complex to start a remarkable juggling act. +; e.g. PRINT 2 * (VAL "2+2" + TAN 3) +; In fact, provided there is enough free RAM, the Spectrum can evaluate +; an expression of unlimited complexity. +; Apart from a couple of minor glitches, which you can now correct, the +; system is remarkably robust. + + +; --------------------------------- +; Scan expression or sub-expression +; --------------------------------- +; +; + +;; SCANNING +L24FB: RST 18H ; GET-CHAR + LD B,$00 ; priority marker zero is pushed on stack + ; to signify end of expression when it is + ; popped off again. + PUSH BC ; put in on stack. + ; and proceed to consider the first character + ; of the expression. + +;; S-LOOP-1 +L24FF: LD C,A ; store the character while a look up is done. + LD HL,L2596 ; Address: scan-func + CALL L16DC ; routine INDEXER is called to see if it is + ; part of a limited range '+', '(', 'ATTR' etc. + + LD A,C ; fetch the character back + JP NC,L2684 ; jump forward to S-ALPHNUM if not in primary + ; operators and functions to consider in the + ; first instance a digit or a variable and + ; then anything else. >>> + + LD B,$00 ; but here if it was found in table so + LD C,(HL) ; fetch offset from table and make B zero. + ADD HL,BC ; add the offset to position found + JP (HL) ; and jump to the routine e.g. S-BIN + ; making an indirect exit from there. + +; ------------------------------------------------------------------------- +; The four service subroutines for routines in the scannings function table +; ------------------------------------------------------------------------- + +; PRINT """Hooray!"" he cried." + +;; S-QUOTE-S +L250F: CALL L0074 ; routine CH-ADD+1 points to next character + ; and fetches that character. + INC BC ; increase length counter. + CP $0D ; is it carriage return ? + ; inside a quote. + JP Z,L1C8A ; jump back to REPORT-C if so. + ; 'Nonsense in BASIC'. + + CP $22 ; is it a quote '"' ? + JR NZ,L250F ; back to S-QUOTE-S if not for more. + + CALL L0074 ; routine CH-ADD+1 + CP $22 ; compare with possible adjacent quote + RET ; return. with zero set if two together. + +; --- + +; This subroutine is used to get two coordinate expressions for the three +; functions SCREEN$, ATTR and POINT that have two fixed parameters and +; therefore require surrounding braces. + +;; S-2-COORD +L2522: RST 20H ; NEXT-CHAR + CP $28 ; is it the opening '(' ? + JR NZ,L252D ; forward to S-RPORT-C if not + ; 'Nonsense in BASIC'. + + CALL L1C79 ; routine NEXT-2NUM gets two comma-separated + ; numeric expressions. Note. this could cause + ; many more recursive calls to SCANNING but + ; the parent function will be evaluated fully + ; before rejoining the main juggling act. + + RST 18H ; GET-CHAR + CP $29 ; is it the closing ')' ? + +;; S-RPORT-C +L252D: JP NZ,L1C8A ; jump back to REPORT-C if not. + ; 'Nonsense in BASIC'. + +; ------------ +; Check syntax +; ------------ +; This routine is called on a number of occasions to check if syntax is being +; checked or if the program is being run. To test the flag inline would use +; four bytes of code, but a call instruction only uses 3 bytes of code. + +;; SYNTAX-Z +L2530: BIT 7,(IY+$01) ; test FLAGS - checking syntax only ? + RET ; return. + +; ---------------- +; Scanning SCREEN$ +; ---------------- +; This function returns the code of a bit-mapped character at screen +; position at line C, column B. It is unable to detect the mosaic characters +; which are not bit-mapped but detects the ASCII 32 - 127 range. +; The bit-mapped UDGs are ignored which is curious as it requires only a +; few extra bytes of code. As usual, anything to do with CHARS is weird. +; If no match is found a null string is returned. +; No actual check on ranges is performed - that's up to the BASIC programmer. +; No real harm can come from SCREEN$(255,255) although the BASIC manual +; says that invalid values will be trapped. +; Interestingly, in the Pitman pocket guide, 1984, Vickers says that the +; range checking will be performed. + +;; S-SCRN$-S +L2535: CALL L2307 ; routine STK-TO-BC. + LD HL,($5C36) ; fetch address of CHARS. + LD DE,$0100 ; fetch offset to chr$ 32 + ADD HL,DE ; and find start of bitmaps. + ; Note. not inc h. ?? + LD A,C ; transfer line to A. + RRCA ; multiply + RRCA ; by + RRCA ; thirty-two. + AND $E0 ; and with 11100000 + XOR B ; combine with column $00 - $1F + LD E,A ; to give the low byte of top line + LD A,C ; column to A range 00000000 to 00011111 + AND $18 ; and with 00011000 + XOR $40 ; xor with 01000000 (high byte screen start) + LD D,A ; register DE now holds start address of cell. + LD B,$60 ; there are 96 characters in ASCII set. + +;; S-SCRN-LP +L254F: PUSH BC ; save count + PUSH DE ; save screen start address + PUSH HL ; save bitmap start + LD A,(DE) ; first byte of screen to A + XOR (HL) ; xor with corresponding character byte + JR Z,L255A ; forward to S-SC-MTCH if they match + ; if inverse result would be $FF + ; if any other then mismatch + + INC A ; set to $00 if inverse + JR NZ,L2573 ; forward to S-SCR-NXT if a mismatch + + DEC A ; restore $FF + +; a match has been found so seven more to test. + +;; S-SC-MTCH +L255A: LD C,A ; load C with inverse mask $00 or $FF + LD B,$07 ; count seven more bytes + +;; S-SC-ROWS +L255D: INC D ; increment screen address. + INC HL ; increment bitmap address. + LD A,(DE) ; byte to A + XOR (HL) ; will give $00 or $FF (inverse) + XOR C ; xor with inverse mask + JR NZ,L2573 ; forward to S-SCR-NXT if no match. + + DJNZ L255D ; back to S-SC-ROWS until all eight matched. + +; continue if a match of all eight bytes was found + + POP BC ; discard the + POP BC ; saved + POP BC ; pointers + LD A,$80 ; the endpoint of character set + SUB B ; subtract the counter + ; to give the code 32-127 + LD BC,$0001 ; make one space in workspace. + + RST 30H ; BC-SPACES creates the space sliding + ; the calculator stack upwards. + LD (DE),A ; start is addressed by DE, so insert code + JR L257D ; forward to S-SCR-STO + +; --- + +; the jump was here if no match and more bitmaps to test. + +;; S-SCR-NXT +L2573: POP HL ; restore the last bitmap start + LD DE,$0008 ; and prepare to add 8. + ADD HL,DE ; now addresses next character bitmap. + POP DE ; restore screen address + POP BC ; and character counter in B + DJNZ L254F ; back to S-SCRN-LP if more characters. + + LD C,B ; B is now zero, so BC now zero. + +;; S-SCR-STO +L257D: JP L2AB2 ; to STK-STO-$ to store the string in + ; workspace or a string with zero length. + ; (value of DE doesn't matter in last case) + +; Note. this exit seems correct but the general-purpose routine S-STRING +; that calls this one will also stack any of its string results so this +; leads to a double storing of the result in this case. +; The instruction at L257D should just be a RET. +; credit Stephen Kelly and others, 1982. + +; ------------- +; Scanning ATTR +; ------------- +; This function subroutine returns the attributes of a screen location - +; a numeric result. +; Again it's up to the BASIC programmer to supply valid values of line/column. + +;; S-ATTR-S +L2580: CALL L2307 ; routine STK-TO-BC fetches line to C, + ; and column to B. + LD A,C ; line to A $00 - $17 (max 00010111) + RRCA ; rotate + RRCA ; bits + RRCA ; left. + LD C,A ; store in C as an intermediate value. + + AND $E0 ; pick up bits 11100000 ( was 00011100 ) + XOR B ; combine with column $00 - $1F + LD L,A ; low byte now correct. + + LD A,C ; bring back intermediate result from C + AND $03 ; mask to give correct third of + ; screen $00 - $02 + XOR $58 ; combine with base address. + LD H,A ; high byte correct. + LD A,(HL) ; pick up the colour attribute. + JP L2D28 ; forward to STACK-A to store result + ; and make an indirect exit. + +; ----------------------- +; Scanning function table +; ----------------------- +; This table is used by INDEXER routine to find the offsets to +; four operators and eight functions. e.g. $A8 is the token 'FN'. +; This table is used in the first instance for the first character of an +; expression or by a recursive call to SCANNING for the first character of +; any sub-expression. It eliminates functions that have no argument or +; functions that can have more than one argument and therefore require +; braces. By eliminating and dealing with these now it can later take a +; simplistic approach to all other functions and assume that they have +; one argument. +; Similarly by eliminating BIN and '.' now it is later able to assume that +; all numbers begin with a digit and that the presence of a number or +; variable can be detected by a call to ALPHANUM. +; By default all expressions are positive and the spurious '+' is eliminated +; now as in print +2. This should not be confused with the operator '+'. +; Note. this does allow a degree of nonsense to be accepted as in +; PRINT +"3 is the greatest.". +; An acquired programming skill is the ability to include brackets where +; they are not necessary. +; A bracket at the start of a sub-expression may be spurious or necessary +; to denote that the contained expression is to be evaluated as an entity. +; In either case this is dealt with by recursive calls to SCANNING. +; An expression that begins with a quote requires special treatment. + +;; scan-func +L2596: DEFB $22, L25B3-$-1 ; $1C offset to S-QUOTE + DEFB '(', L25E8-$-1 ; $4F offset to S-BRACKET + DEFB '.', L268D-$-1 ; $F2 offset to S-DECIMAL + DEFB '+', L25AF-$-1 ; $12 offset to S-U-PLUS + + DEFB $A8, L25F5-$-1 ; $56 offset to S-FN + DEFB $A5, L25F8-$-1 ; $57 offset to S-RND + DEFB $A7, L2627-$-1 ; $84 offset to S-PI + DEFB $A6, L2634-$-1 ; $8F offset to S-INKEY$ + DEFB $C4, L268D-$-1 ; $E6 offset to S-BIN + DEFB $AA, L2668-$-1 ; $BF offset to S-SCREEN$ + DEFB $AB, L2672-$-1 ; $C7 offset to S-ATTR + DEFB $A9, L267B-$-1 ; $CE offset to S-POINT + + DEFB $00 ; zero end marker + +; -------------------------- +; Scanning function routines +; -------------------------- +; These are the 11 subroutines accessed by the above table. +; S-BIN and S-DECIMAL are the same +; The 1-byte offset limits their location to within 255 bytes of their +; entry in the table. + +; -> +;; S-U-PLUS +L25AF: RST 20H ; NEXT-CHAR just ignore + JP L24FF ; to S-LOOP-1 + +; --- + +; -> +;; S-QUOTE +L25B3: RST 18H ; GET-CHAR + INC HL ; address next character (first in quotes) + PUSH HL ; save start of quoted text. + LD BC,$0000 ; initialize length of string to zero. + CALL L250F ; routine S-QUOTE-S + JR NZ,L25D9 ; forward to S-Q-PRMS if + +;; S-Q-AGAIN +L25BE: CALL L250F ; routine S-QUOTE-S copies string until a + ; quote is encountered + JR Z,L25BE ; back to S-Q-AGAIN if two quotes WERE + ; together. + +; but if just an isolated quote then that terminates the string. + + CALL L2530 ; routine SYNTAX-Z + JR Z,L25D9 ; forward to S-Q-PRMS if checking syntax. + + + RST 30H ; BC-SPACES creates the space for true + ; copy of string in workspace. + POP HL ; re-fetch start of quoted text. + PUSH DE ; save start in workspace. + +;; S-Q-COPY +L25CB: LD A,(HL) ; fetch a character from source. + INC HL ; advance source address. + LD (DE),A ; place in destination. + INC DE ; advance destination address. + CP $22 ; was it a '"' just copied ? + JR NZ,L25CB ; back to S-Q-COPY to copy more if not + + LD A,(HL) ; fetch adjacent character from source. + INC HL ; advance source address. + CP $22 ; is this '"' ? - i.e. two quotes together ? + JR Z,L25CB ; to S-Q-COPY if so including just one of the + ; pair of quotes. + +; proceed when terminating quote encountered. + +;; S-Q-PRMS +L25D9: DEC BC ; decrease count by 1. + POP DE ; restore start of string in workspace. + +;; S-STRING +L25DB: LD HL,$5C3B ; Address FLAGS system variable. + RES 6,(HL) ; signal string result. + BIT 7,(HL) ; is syntax being checked. + CALL NZ,L2AB2 ; routine STK-STO-$ is called in runtime. + JP L2712 ; jump forward to S-CONT-2 ===> + +; --- + +; -> +;; S-BRACKET +L25E8: RST 20H ; NEXT-CHAR + CALL L24FB ; routine SCANNING is called recursively. + CP $29 ; is it the closing ')' ? + JP NZ,L1C8A ; jump back to REPORT-C if not + ; 'Nonsense in BASIC' + + RST 20H ; NEXT-CHAR + JP L2712 ; jump forward to S-CONT-2 ===> + +; --- + +; -> +;; S-FN +L25F5: JP L27BD ; jump forward to S-FN-SBRN. + +; --- + +; -> +;; S-RND +L25F8: CALL L2530 ; routine SYNTAX-Z + JR Z,L2625 ; forward to S-RND-END if checking syntax. + + LD BC,($5C76) ; fetch system variable SEED + CALL L2D2B ; routine STACK-BC places on calculator stack + + RST 28H ;; FP-CALC ;s. + DEFB $A1 ;;stk-one ;s,1. + DEFB $0F ;;addition ;s+1. + DEFB $34 ;;stk-data ; + DEFB $37 ;;Exponent: $87, + ;;Bytes: 1 + DEFB $16 ;;(+00,+00,+00) ;s+1,75. + DEFB $04 ;;multiply ;(s+1)*75 = v + DEFB $34 ;;stk-data ;v. + DEFB $80 ;;Bytes: 3 + DEFB $41 ;;Exponent $91 + DEFB $00,$00,$80 ;;(+00) ;v,65537. + DEFB $32 ;;n-mod-m ;remainder, result. + DEFB $02 ;;delete ;remainder. + DEFB $A1 ;;stk-one ;remainder, 1. + DEFB $03 ;;subtract ;remainder - 1. = rnd + DEFB $31 ;;duplicate ;rnd,rnd. + DEFB $38 ;;end-calc + + CALL L2DA2 ; routine FP-TO-BC + LD ($5C76),BC ; store in SEED for next starting point. + LD A,(HL) ; fetch exponent + AND A ; is it zero ? + JR Z,L2625 ; forward if so to S-RND-END + + SUB $10 ; reduce exponent by 2^16 + LD (HL),A ; place back + +;; S-RND-END +L2625: JR L2630 ; forward to S-PI-END + +; --- + +; the number PI 3.14159... + +; -> +;; S-PI +L2627: CALL L2530 ; routine SYNTAX-Z + JR Z,L2630 ; to S-PI-END if checking syntax. + + RST 28H ;; FP-CALC + DEFB $A3 ;;stk-pi/2 pi/2. + DEFB $38 ;;end-calc + + INC (HL) ; increment the exponent leaving pi + ; on the calculator stack. + +;; S-PI-END +L2630: RST 20H ; NEXT-CHAR + JP L26C3 ; jump forward to S-NUMERIC + +; --- + +; -> +;; S-INKEY$ +L2634: LD BC,$105A ; priority $10, operation code $1A ('read-in') + ; +$40 for string result, numeric operand. + ; set this up now in case we need to use the + ; calculator. + RST 20H ; NEXT-CHAR + CP $23 ; '#' ? + JP Z,L270D ; to S-PUSH-PO if so to use the calculator + ; single operation + ; to read from network/RS232 etc. . + +; else read a key from the keyboard. + + LD HL,$5C3B ; fetch FLAGS + RES 6,(HL) ; signal string result. + BIT 7,(HL) ; checking syntax ? + JR Z,L2665 ; forward to S-INK$-EN if so + + JP L3B6C ; Spectrum 128 patch + +L2649: LD C,$00 ; the length of an empty string + JR NZ,L2660 ; to S-IK$-STK to store empty string if + ; no key returned. + + CALL L031E ; routine K-TEST get main code in A + JR NC,L2660 ; to S-IK$-STK to stack null string if + ; invalid + + DEC D ; D is expected to be FLAGS so set bit 3 $FF + ; 'L' Mode so no keywords. + LD E,A ; main key to A + ; C is MODE 0 'KLC' from above still. + CALL L0333 ; routine K-DECODE +L2657: PUSH AF ; save the code + LD BC,$0001 ; make room for one character + + RST 30H ; BC-SPACES + POP AF ; bring the code back + LD (DE),A ; put the key in workspace + LD C,$01 ; set C length to one + +;; S-IK$-STK +L2660: LD B,$00 ; set high byte of length to zero + CALL L2AB2 ; routine STK-STO-$ + +;; S-INK$-EN +L2665: JP L2712 ; to S-CONT-2 ===> + +; --- + +; -> +;; S-SCREEN$ +L2668: CALL L2522 ; routine S-2-COORD + CALL NZ,L2535 ; routine S-SCRN$-S + + RST 20H ; NEXT-CHAR + JP L25DB ; forward to S-STRING to stack result + +; --- + +; -> +;; S-ATTR +L2672: CALL L2522 ; routine S-2-COORD + CALL NZ,L2580 ; routine S-ATTR-S + + RST 20H ; NEXT-CHAR + JR L26C3 ; forward to S-NUMERIC + +; --- + +; -> +;; S-POINT +L267B: CALL L2522 ; routine S-2-COORD + CALL NZ,L22CB ; routine POINT-SUB + + RST 20H ; NEXT-CHAR + JR L26C3 ; forward to S-NUMERIC + +; ----------------------------- + +; ==> The branch was here if not in table. + +;; S-ALPHNUM +L2684: CALL L2C88 ; routine ALPHANUM checks if variable or + ; a digit. + JR NC,L26DF ; forward to S-NEGATE if not to consider + ; a '-' character then functions. + + CP $41 ; compare 'A' + JR NC,L26C9 ; forward to S-LETTER if alpha -> + ; else must have been numeric so continue + ; into that routine. + +; This important routine is called during runtime and from LINE-SCAN +; when a BASIC line is checked for syntax. It is this routine that +; inserts, during syntax checking, the invisible floating point numbers +; after the numeric expression. During runtime it just picks these +; numbers up. It also handles BIN format numbers. + +; -> +;; S-BIN +;; S-DECIMAL +L268D: CALL L2530 ; routine SYNTAX-Z + JR NZ,L26B5 ; to S-STK-DEC in runtime + +; this route is taken when checking syntax. + + CALL L2C9B ; routine DEC-TO-FP to evaluate number + + RST 18H ; GET-CHAR to fetch HL + LD BC,$0006 ; six locations required + CALL L1655 ; routine MAKE-ROOM + INC HL ; to first new location + LD (HL),$0E ; insert number marker + INC HL ; address next + EX DE,HL ; make DE destination. + LD HL,($5C65) ; STKEND points to end of stack. + LD C,$05 ; result is five locations lower + AND A ; prepare for true subtraction + SBC HL,BC ; point to start of value. + LD ($5C65),HL ; update STKEND as we are taking number. + LDIR ; Copy five bytes to program location + EX DE,HL ; transfer pointer to HL + DEC HL ; adjust + CALL L0077 ; routine TEMP-PTR1 sets CH-ADD + JR L26C3 ; to S-NUMERIC to record nature of result + +; --- + +; branch here in runtime. + +;; S-STK-DEC +L26B5: RST 18H ; GET-CHAR positions HL at digit. + +;; S-SD-SKIP +L26B6: INC HL ; advance pointer + LD A,(HL) ; until we find + CP $0E ; chr 14d - the number indicator + JR NZ,L26B6 ; to S-SD-SKIP until a match + ; it has to be here. + + INC HL ; point to first byte of number + CALL L33B4 ; routine STACK-NUM stacks it + LD ($5C5D),HL ; update system variable CH_ADD + +;; S-NUMERIC +L26C3: SET 6,(IY+$01) ; update FLAGS - Signal numeric result + JR L26DD ; forward to S-CONT-1 ===> + ; actually S-CONT-2 is destination but why + ; waste a byte on a jump when a JR will do. + ; actually a JR L2712 can be used. Rats. + +; end of functions accessed from scanning functions table. + +; -------------------------- +; Scanning variable routines +; -------------------------- +; +; + +;; S-LETTER +L26C9: CALL L28B2 ; routine LOOK-VARS + JP C,L1C2E ; jump back to REPORT-2 if not found + ; 'Variable not found' + ; but a variable is always 'found' if syntax + ; is being checked. + + CALL Z,L2996 ; routine STK-VAR considers a subscript/slice + LD A,($5C3B) ; fetch FLAGS value + CP $C0 ; compare 11000000 + JR C,L26DD ; step forward to S-CONT-1 if string ===> + + INC HL ; advance pointer + CALL L33B4 ; routine STACK-NUM + +;; S-CONT-1 +L26DD: JR L2712 ; forward to S-CONT-2 ===> + +; ---------------------------------------- +; -> the scanning branch was here if not alphanumeric. +; All the remaining functions will be evaluated by a single call to the +; calculator. The correct priority for the operation has to be placed in +; the B register and the operation code, calculator literal in the C register. +; the operation code has bit 7 set if result is numeric and bit 6 is +; set if operand is numeric. so +; $C0 = numeric result, numeric operand. e.g. 'sin' +; $80 = numeric result, string operand. e.g. 'code' +; $40 = string result, numeric operand. e.g. 'str$' +; $00 = string result, string operand. e.g. 'val$' + +;; S-NEGATE +L26DF: LD BC,$09DB ; prepare priority 09, operation code $C0 + + ; 'negate' ($1B) - bits 6 and 7 set for numeric + ; result and numeric operand. + + CP $2D ; is it '-' ? + JR Z,L270D ; forward if so to S-PUSH-PO + + LD BC,$1018 ; prepare priority $10, operation code 'val$' - + ; bits 6 and 7 reset for string result and + ; string operand. + + CP $AE ; is it 'VAL$' ? + JR Z,L270D ; forward if so to S-PUSH-PO + + SUB $AF ; subtract token 'CODE' value to reduce + ; functions 'CODE' to 'NOT' although the + ; upper range is, as yet, unchecked. + ; valid range would be $00 - $14. + + JP C,L1C8A ; jump back to REPORT-C with anything else + ; 'Nonsense in BASIC' + + LD BC,$04F0 ; prepare priority $04, operation $C0 + + ; 'not' ($30) + + CP $14 ; is it 'NOT' + JR Z,L270D ; forward to S-PUSH-PO if so + + JP NC,L1C8A ; to REPORT-C if higher + ; 'Nonsense in BASIC' + + LD B,$10 ; priority $10 for all the rest + ADD A,$DC ; make range $DC - $EF + ; $C0 + 'code'($1C) thru 'chr$' ($2F) + + LD C,A ; transfer 'function' to C + CP $DF ; is it 'sin' ? + JR NC,L2707 ; forward to S-NO-TO-$ with 'sin' through + ; 'chr$' as operand is numeric. + +; all the rest 'cos' through 'chr$' give a numeric result except 'str$' +; and 'chr$'. + + RES 6,C ; signal string operand for 'code', 'val' and + ; 'len'. + +;; S-NO-TO-$ +L2707: CP $EE ; compare 'str$' + JR C,L270D ; forward to S-PUSH-PO if lower as result + ; is numeric. + + RES 7,C ; reset bit 7 of op code for 'str$', 'chr$' + ; as result is string. + +; >> This is where they were all headed for. + +;; S-PUSH-PO +L270D: PUSH BC ; push the priority and calculator operation + ; code. + + RST 20H ; NEXT-CHAR + JP L24FF ; jump back to S-LOOP-1 to go round the loop + ; again with the next character. + +; -------------------------------- + +; ===> there were many branches forward to here + +;; S-CONT-2 +L2712: RST 18H ; GET-CHAR + +;; S-CONT-3 +L2713: CP $28 ; is it '(' ? + JR NZ,L2723 ; forward to S-OPERTR if not > + + BIT 6,(IY+$01) ; test FLAGS - numeric or string result ? + JR NZ,L2734 ; forward to S-LOOP if numeric to evaluate > + +; if a string preceded '(' then slice it. + + CALL L2A52 ; routine SLICING + + RST 20H ; NEXT-CHAR + JR L2713 ; back to S-CONT-3 + +; --------------------------- + +; the branch was here when possibility of an operator '(' has been excluded. + +;; S-OPERTR +L2723: LD B,$00 ; prepare to add + LD C,A ; possible operator to C + LD HL,L2795 ; Address: $2795 - tbl-of-ops + CALL L16DC ; routine INDEXER + JR NC,L2734 ; forward to S-LOOP if not in table + +; but if found in table the priority has to be looked up. + + LD C,(HL) ; operation code to C ( B is still zero ) + LD HL,L27B0 - $C3 ; $26ED is base of table + ADD HL,BC ; index into table. + LD B,(HL) ; priority to B. + +; ------------------ +; Scanning main loop +; ------------------ +; the juggling act + +;; S-LOOP +L2734: POP DE ; fetch last priority and operation + LD A,D ; priority to A + CP B ; compare with this one + JR C,L2773 ; forward to S-TIGHTER to execute the + ; last operation before this one as it has + ; higher priority. + +; the last priority was greater or equal this one. + + AND A ; if it is zero then so is this + JP Z,L0018 ; jump to exit via get-char pointing at + ; next character. + ; This may be the character after the + ; expression or, if exiting a recursive call, + ; the next part of the expression to be + ; evaluated. + + PUSH BC ; save current priority/operation + ; as it has lower precedence than the one + ; now in DE. + +; the 'USR' function is special in that it is overloaded to give two types +; of result. + + LD HL,$5C3B ; address FLAGS + LD A,E ; new operation to A register + CP $ED ; is it $C0 + 'usr-no' ($2D) ? + JR NZ,L274C ; forward to S-STK-LST if not + + BIT 6,(HL) ; string result expected ? + ; (from the lower priority operand we've + ; just pushed on stack ) + JR NZ,L274C ; forward to S-STK-LST if numeric + ; as operand bits match. + + LD E,$99 ; reset bit 6 and substitute $19 'usr-$' + ; for string operand. + +;; S-STK-LST +L274C: PUSH DE ; now stack this priority/operation + CALL L2530 ; routine SYNTAX-Z + JR Z,L275B ; forward to S-SYNTEST if checking syntax. + + LD A,E ; fetch the operation code + AND $3F ; mask off the result/operand bits to leave + ; a calculator literal. + LD B,A ; transfer to B register + +; now use the calculator to perform the single operation - operand is on +; the calculator stack. +; Note. although the calculator is performing a single operation most +; functions e.g. TAN are written using other functions and literals and +; these in turn are written using further strings of calculator literals so +; another level of magical recursion joins the juggling act for a while +; as the calculator too is calling itself. + + RST 28H ;; FP-CALC + DEFB $3B ;;fp-calc-2 +L2758: DEFB $38 ;;end-calc + + JR L2764 ; forward to S-RUNTEST + +; --- + +; the branch was here if checking syntax only. + +;; S-SYNTEST +L275B: LD A,E ; fetch the operation code to accumulator + XOR (IY+$01) ; compare with bits of FLAGS + AND $40 ; bit 6 will be zero now if operand + ; matched expected result. + +;; S-RPORT-C2 +L2761: JP NZ,L1C8A ; to REPORT-C if mismatch + ; 'Nonsense in BASIC' + ; else continue to set flags for next + +; the branch is to here in runtime after a successful operation. + +;; S-RUNTEST +L2764: POP DE ; fetch the last operation from stack + LD HL,$5C3B ; address FLAGS + SET 6,(HL) ; set default to numeric result in FLAGS + BIT 7,E ; test the operational result + JR NZ,L2770 ; forward to S-LOOPEND if numeric + + RES 6,(HL) ; reset bit 6 of FLAGS to show string result. + +;; S-LOOPEND +L2770: POP BC ; fetch the previous priority/operation + JR L2734 ; back to S-LOOP to perform these + +; --- + +; the branch was here when a stacked priority/operator had higher priority +; than the current one. + +;; S-TIGHTER +L2773: PUSH DE ; save high priority op on stack again + LD A,C ; fetch lower priority operation code + BIT 6,(IY+$01) ; test FLAGS - Numeric or string result ? + JR NZ,L2790 ; forward to S-NEXT if numeric result + +; if this is lower priority yet has string then must be a comparison. +; Since these can only be evaluated in context and were defaulted to +; numeric in operator look up they must be changed to string equivalents. + + AND $3F ; mask to give true calculator literal + ADD A,$08 ; augment numeric literals to string + ; equivalents. + ; 'no-&-no' => 'str-&-no' + ; 'no-l-eql' => 'str-l-eql' + ; 'no-gr-eq' => 'str-gr-eq' + ; 'nos-neql' => 'strs-neql' + ; 'no-grtr' => 'str-grtr' + ; 'no-less' => 'str-less' + ; 'nos-eql' => 'strs-eql' + ; 'addition' => 'strs-add' + LD C,A ; put modified comparison operator back + CP $10 ; is it now 'str-&-no' ? + JR NZ,L2788 ; forward to S-NOT-AND if not. + + SET 6,C ; set numeric operand bit + JR L2790 ; forward to S-NEXT + +; --- + +;; S-NOT-AND +L2788: JR C,L2761 ; back to S-RPORT-C2 if less + ; 'Nonsense in BASIC'. + ; e.g. a$ * b$ + + CP $17 ; is it 'strs-add' ? + JR Z,L2790 ; forward to to S-NEXT if so + ; (bit 6 and 7 are reset) + + SET 7,C ; set numeric (Boolean) result for all others + +;; S-NEXT +L2790: PUSH BC ; now save this priority/operation on stack + + RST 20H ; NEXT-CHAR + JP L24FF ; jump back to S-LOOP-1 + +; ------------------ +; Table of operators +; ------------------ +; This table is used to look up the calculator literals associated with +; the operator character. The thirteen calculator operations $03 - $0F +; have bits 6 and 7 set to signify a numeric result. +; Some of these codes and bits may be altered later if the context suggests +; a string comparison or operation. +; that is '+', '=', '>', '<', '<=', '>=' or '<>'. + +;; tbl-of-ops +L2795: DEFB '+', $CF ; $C0 + 'addition' + DEFB '-', $C3 ; $C0 + 'subtract' + DEFB '*', $C4 ; $C0 + 'multiply' + DEFB '/', $C5 ; $C0 + 'division' + DEFB '^', $C6 ; $C0 + 'to-power' + DEFB '=', $CE ; $C0 + 'nos-eql' + DEFB '>', $CC ; $C0 + 'no-grtr' + DEFB '<', $CD ; $C0 + 'no-less' + + DEFB $C7, $C9 ; '<=' $C0 + 'no-l-eql' + DEFB $C8, $CA ; '>=' $C0 + 'no-gr-eql' + DEFB $C9, $CB ; '<>' $C0 + 'nos-neql' + DEFB $C5, $C7 ; 'OR' $C0 + 'or' + DEFB $C6, $C8 ; 'AND' $C0 + 'no-&-no' + + DEFB $00 ; zero end-marker. + + +; ------------------- +; Table of priorities +; ------------------- +; This table is indexed with the operation code obtained from the above +; table $C3 - $CF to obtain the priority for the respective operation. + +;; tbl-priors +L27B0: DEFB $06 ; '-' opcode $C3 + DEFB $08 ; '*' opcode $C4 + DEFB $08 ; '/' opcode $C5 + DEFB $0A ; '^' opcode $C6 + DEFB $02 ; 'OR' opcode $C7 + DEFB $03 ; 'AND' opcode $C8 + DEFB $05 ; '<=' opcode $C9 + DEFB $05 ; '>=' opcode $CA + DEFB $05 ; '<>' opcode $CB + DEFB $05 ; '>' opcode $CC + DEFB $05 ; '<' opcode $CD + DEFB $05 ; '=' opcode $CE + DEFB $06 ; '+' opcode $CF + +; ---------------------- +; Scanning function (FN) +; ---------------------- +; This routine deals with user-defined functions. +; The definition can be anywhere in the program area but these are best +; placed near the start of the program as we shall see. +; The evaluation process is quite complex as the Spectrum has to parse two +; statements at the same time. Syntax of both has been checked previously +; and hidden locations have been created immediately after each argument +; of the DEF FN statement. Each of the arguments of the FN function is +; evaluated by SCANNING and placed in the hidden locations. Then the +; expression to the right of the DEF FN '=' is evaluated by SCANNING and for +; any variables encountered, a search is made in the DEF FN variable list +; in the program area before searching in the normal variables area. +; +; Recursion is not allowed: i.e. the definition of a function should not use +; the same function, either directly or indirectly ( through another function). +; You'll normally get error 4, ('Out of memory'), although sometimes the sytem +; will crash. - Vickers, Pitman 1984. +; +; As the definition is just an expression, there would seem to be no means +; of breaking out of such recursion. +; However, by the clever use of string expressions and VAL, such recursion is +; possible. +; e.g. DEF FN a(n) = VAL "n+FN a(n-1)+0" ((n<1) * 10 + 1 TO ) +; will evaluate the full 11-character expression for all values where n is +; greater than zero but just the 11th character, "0", when n drops to zero +; thereby ending the recursion producing the correct result. +; Recursive string functions are possible using VAL$ instead of VAL and the +; null string as the final addend. +; - from a turn of the century newsgroup discussion initiated by Mike Wynne. + +;; S-FN-SBRN +L27BD: CALL L2530 ; routine SYNTAX-Z + JR NZ,L27F7 ; forward to SF-RUN in runtime + + + RST 20H ; NEXT-CHAR + CALL L2C8D ; routine ALPHA check for letters A-Z a-z + JP NC,L1C8A ; jump back to REPORT-C if not + ; 'Nonsense in BASIC' + + + RST 20H ; NEXT-CHAR + CP $24 ; is it '$' ? + PUSH AF ; save character and flags + JR NZ,L27D0 ; forward to SF-BRKT-1 with numeric function + + + RST 20H ; NEXT-CHAR + +;; SF-BRKT-1 +L27D0: CP $28 ; is '(' ? + JR NZ,L27E6 ; forward to SF-RPRT-C if not + ; 'Nonsense in BASIC' + + + RST 20H ; NEXT-CHAR + CP $29 ; is it ')' ? + JR Z,L27E9 ; forward to SF-FLAG-6 if no arguments. + +;; SF-ARGMTS +L27D9: CALL L24FB ; routine SCANNING checks each argument + ; which may be an expression. + + RST 18H ; GET-CHAR + CP $2C ; is it a ',' ? + JR NZ,L27E4 ; forward if not to SF-BRKT-2 to test bracket + + + RST 20H ; NEXT-CHAR if a comma was found + JR L27D9 ; back to SF-ARGMTS to parse all arguments. + +; --- + +;; SF-BRKT-2 +L27E4: CP $29 ; is character the closing ')' ? + +;; SF-RPRT-C +L27E6: JP NZ,L1C8A ; jump to REPORT-C + ; 'Nonsense in BASIC' + +; at this point any optional arguments have had their syntax checked. + +;; SF-FLAG-6 +L27E9: RST 20H ; NEXT-CHAR + LD HL,$5C3B ; address system variable FLAGS + RES 6,(HL) ; signal string result + POP AF ; restore test against '$'. + JR Z,L27F4 ; forward to SF-SYN-EN if string function. + + SET 6,(HL) ; signal numeric result + +;; SF-SYN-EN +L27F4: JP L2712 ; jump back to S-CONT-2 to continue scanning. + +; --- + +; the branch was here in runtime. + +;; SF-RUN +L27F7: RST 20H ; NEXT-CHAR fetches name + AND $DF ; AND 11101111 - reset bit 5 - upper-case. + LD B,A ; save in B + + RST 20H ; NEXT-CHAR + SUB $24 ; subtract '$' + LD C,A ; save result in C + JR NZ,L2802 ; forward if not '$' to SF-ARGMT1 + + RST 20H ; NEXT-CHAR advances to bracket + +;; SF-ARGMT1 +L2802: RST 20H ; NEXT-CHAR advances to start of argument + PUSH HL ; save address + LD HL,($5C53) ; fetch start of program area from PROG + DEC HL ; the search starting point is the previous + ; location. + +;; SF-FND-DF +L2808: LD DE,$00CE ; search is for token 'DEF FN' in E, + ; statement count in D. + PUSH BC ; save C the string test, and B the letter. + CALL L1D86 ; routine LOOK-PROG will search for token. + POP BC ; restore BC. + JR NC,L2814 ; forward to SF-CP-DEF if a match was found. + + +;; REPORT-P +L2812: RST 08H ; ERROR-1 + DEFB $18 ; Error Report: FN without DEF + +;; SF-CP-DEF +L2814: PUSH HL ; save address of DEF FN + CALL L28AB ; routine FN-SKPOVR skips over white-space etc. + ; without disturbing CH-ADD. + AND $DF ; make fetched character upper-case. + CP B ; compare with FN name + JR NZ,L2825 ; forward to SF-NOT-FD if no match. + +; the letters match so test the type. + + CALL L28AB ; routine FN-SKPOVR skips white-space + SUB $24 ; subtract '$' from fetched character + CP C ; compare with saved result of same operation + ; on FN name. + JR Z,L2831 ; forward to SF-VALUES with a match. + +; the letters matched but one was string and the other numeric. + +;; SF-NOT-FD +L2825: POP HL ; restore search point. + DEC HL ; make location before + LD DE,$0200 ; the search is to be for the end of the + ; current definition - 2 statements forward. + PUSH BC ; save the letter/type + CALL L198B ; routine EACH-STMT steps past rejected + ; definition. + POP BC ; restore letter/type + JR L2808 ; back to SF-FND-DF to continue search + +; --- + +; Success! +; the branch was here with matching letter and numeric/string type. + +;; SF-VALUES +L2831: AND A ; test A ( will be zero if string '$' - '$' ) + + CALL Z,L28AB ; routine FN-SKPOVR advances HL past '$'. + + POP DE ; discard pointer to 'DEF FN'. + POP DE ; restore pointer to first FN argument. + LD ($5C5D),DE ; save in CH_ADD + + CALL L28AB ; routine FN-SKPOVR advances HL past '(' + PUSH HL ; save start address in DEF FN *** + CP $29 ; is character a ')' ? + JR Z,L2885 ; forward to SF-R-BR-2 if no arguments. + +;; SF-ARG-LP +L2843: INC HL ; point to next character. + LD A,(HL) ; fetch it. + CP $0E ; is it the number marker + LD D,$40 ; signal numeric in D. + JR Z,L2852 ; forward to SF-ARG-VL if numeric. + + DEC HL ; back to letter + CALL L28AB ; routine FN-SKPOVR skips any white-space + INC HL ; advance past the expected '$' to + ; the 'hidden' marker. + LD D,$00 ; signal string. + +;; SF-ARG-VL +L2852: INC HL ; now address first of 5-byte location. + PUSH HL ; save address in DEF FN statement + PUSH DE ; save D - result type + + CALL L24FB ; routine SCANNING evaluates expression in + ; the FN statement setting FLAGS and leaving + ; result as last value on calculator stack. + + POP AF ; restore saved result type to A + + XOR (IY+$01) ; xor with FLAGS + AND $40 ; and with 01000000 to test bit 6 + JR NZ,L288B ; forward to REPORT-Q if type mismatch. + ; 'Parameter error' + + POP HL ; pop the start address in DEF FN statement + EX DE,HL ; transfer to DE ?? pop straight into de ? + + LD HL,($5C65) ; set HL to STKEND location after value + LD BC,$0005 ; five bytes to move + SBC HL,BC ; decrease HL by 5 to point to start. + LD ($5C65),HL ; set STKEND 'removing' value from stack. + + LDIR ; copy value into DEF FN statement + EX DE,HL ; set HL to location after value in DEF FN + DEC HL ; step back one + CALL L28AB ; routine FN-SKPOVR gets next valid character + CP $29 ; is it ')' end of arguments ? + JR Z,L2885 ; forward to SF-R-BR-2 if so. + +; a comma separator has been encountered in the DEF FN argument list. + + PUSH HL ; save position in DEF FN statement + + RST 18H ; GET-CHAR from FN statement + CP $2C ; is it ',' ? + JR NZ,L288B ; forward to REPORT-Q if not + ; 'Parameter error' + + RST 20H ; NEXT-CHAR in FN statement advances to next + ; argument. + + POP HL ; restore DEF FN pointer + CALL L28AB ; routine FN-SKPOVR advances to corresponding + ; argument. + + JR L2843 ; back to SF-ARG-LP looping until all + ; arguments are passed into the DEF FN + ; hidden locations. + +; --- + +; the branch was here when all arguments passed. + +;; SF-R-BR-2 +L2885: PUSH HL ; save location of ')' in DEF FN + + RST 18H ; GET-CHAR gets next character in FN + CP $29 ; is it a ')' also ? + JR Z,L288D ; forward to SF-VALUE if so. + + +;; REPORT-Q +L288B: RST 08H ; ERROR-1 + DEFB $19 ; Error Report: Parameter error + +;; SF-VALUE +L288D: POP DE ; location of ')' in DEF FN to DE. + EX DE,HL ; now to HL, FN ')' pointer to DE. + LD ($5C5D),HL ; initialize CH_ADD to this value. + +; At this point the start of the DEF FN argument list is on the machine stack. +; We also have to consider that this defined function may form part of the +; definition of another defined function (though not itself). +; As this defined function may be part of a hierarchy of defined functions +; currently being evaluated by recursive calls to SCANNING, then we have to +; preserve the original value of DEFADD and not assume that it is zero. + + LD HL,($5C0B) ; get original DEFADD address + EX (SP),HL ; swap with DEF FN address on stack *** + LD ($5C0B),HL ; set DEFADD to point to this argument list + ; during scanning. + + PUSH DE ; save FN ')' pointer. + + RST 20H ; NEXT-CHAR advances past ')' in define + + RST 20H ; NEXT-CHAR advances past '=' to expression + + CALL L24FB ; routine SCANNING evaluates but searches + ; initially for variables at DEFADD + + POP HL ; pop the FN ')' pointer + LD ($5C5D),HL ; set CH_ADD to this + POP HL ; pop the original DEFADD value + LD ($5C0B),HL ; and re-insert into DEFADD system variable. + + RST 20H ; NEXT-CHAR advances to character after ')' + JP L2712 ; to S-CONT-2 - to continue current + ; invocation of scanning + +; -------------------- +; Used to parse DEF FN +; -------------------- +; e.g. DEF FN s $ ( x ) = b $ ( TO x ) : REM exaggerated +; +; This routine is used 10 times to advance along a DEF FN statement +; skipping spaces and colour control codes. It is similar to NEXT-CHAR +; which is, at the same time, used to skip along the corresponding FN function +; except the latter has to deal with AT and TAB characters in string +; expressions. These cannot occur in a program area so this routine is +; simpler as both colour controls and their parameters are less than space. + +;; FN-SKPOVR +L28AB: INC HL ; increase pointer + LD A,(HL) ; fetch addressed character + CP $21 ; compare with space + 1 + JR C,L28AB ; back to FN-SKPOVR if less + + RET ; return pointing to a valid character. + +; --------- +; LOOK-VARS +; --------- +; +; + +;; LOOK-VARS +L28B2: SET 6,(IY+$01) ; update FLAGS - presume numeric result + + RST 18H ; GET-CHAR + CALL L2C8D ; routine ALPHA tests for A-Za-z + JP NC,L1C8A ; jump to REPORT-C if not. + ; 'Nonsense in BASIC' + + PUSH HL ; save pointer to first letter ^1 + AND $1F ; mask lower bits, 1 - 26 decimal 000xxxxx + LD C,A ; store in C. + + RST 20H ; NEXT-CHAR + PUSH HL ; save pointer to second character ^2 + CP $28 ; is it '(' - an array ? + JR Z,L28EF ; forward to V-RUN/SYN if so. + + SET 6,C ; set 6 signaling string if solitary 010 + CP $24 ; is character a '$' ? + JR Z,L28DE ; forward to V-STR-VAR + + SET 5,C ; signal numeric 011 + CALL L2C88 ; routine ALPHANUM sets carry if second + ; character is alphanumeric. + JR NC,L28E3 ; forward to V-TEST-FN if just one character + +; it is more than one character but re-test current character so that 6 reset +; Note. this is a rare lack of elegance. Bit 6 could be reset once before +; entering the loop. Another puzzle is that this loop renders the similar +; loop at V-PASS redundant. + +;; V-CHAR +L28D4: CALL L2C88 ; routine ALPHANUM + JR NC,L28EF ; to V-RUN/SYN when no more + + RES 6,C ; make long named type 001 + + RST 20H ; NEXT-CHAR + JR L28D4 ; loop back to V-CHAR + +; --- + + +;; V-STR-VAR +L28DE: RST 20H ; NEXT-CHAR advances past '$' + RES 6,(IY+$01) ; update FLAGS - signal string result. + +;; V-TEST-FN +L28E3: LD A,($5C0C) ; load A with DEFADD_hi + AND A ; and test for zero. + JR Z,L28EF ; forward to V-RUN/SYN if a defined function + ; is not being evaluated. + +; Note. + + CALL L2530 ; routine SYNTAX-Z + JP NZ,L2951 ; JUMP to STK-F-ARG in runtime and then + ; back to this point if no variable found. + +;; V-RUN/SYN +L28EF: LD B,C ; save flags in B + CALL L2530 ; routine SYNTAX-Z + JR NZ,L28FD ; to V-RUN to look for the variable in runtime + +; if checking syntax the letter is not returned + + LD A,C ; copy letter/flags to A + AND $E0 ; and with 11100000 to get rid of the letter + SET 7,A ; use spare bit to signal checking syntax. + LD C,A ; and transfer to C. + JR L2934 ; forward to V-SYNTAX + +; --- + +; but in runtime search for the variable. + +;; V-RUN +L28FD: LD HL,($5C4B) ; set HL to start of variables from VARS + +;; V-EACH +L2900: LD A,(HL) ; get first character + AND $7F ; and with 01111111 + ; ignoring bit 7 which distinguishes + ; arrays or for/next variables. + + JR Z,L2932 ; to V-80-BYTE if zero as must be 10000000 + ; the variables end-marker. + + CP C ; compare with supplied value. + JR NZ,L292A ; forward to V-NEXT if no match. + + RLA ; destructively test + ADD A,A ; bits 5 and 6 of A + ; jumping if bit 5 reset or 6 set + + JP P,L293F ; to V-FOUND-2 strings and arrays + + JR C,L293F ; to V-FOUND-2 simple and for next + +; leaving long name variables. + + POP DE ; pop pointer to 2nd. char + PUSH DE ; save it again + PUSH HL ; save variable first character pointer + +;; V-MATCHES +L2912: INC HL ; address next character in vars area + +;; V-SPACES +L2913: LD A,(DE) ; pick up letter from prog area + INC DE ; and advance address + CP $20 ; is it a space + JR Z,L2913 ; back to V-SPACES until non-space + + OR $20 ; convert to range 1 - 26. + CP (HL) ; compare with addressed variables character + JR Z,L2912 ; loop back to V-MATCHES if a match on an + ; intermediate letter. + + OR $80 ; now set bit 7 as last character of long + ; names are inverted. + CP (HL) ; compare again + JR NZ,L2929 ; forward to V-GET-PTR if no match + +; but if they match check that this is also last letter in prog area + + LD A,(DE) ; fetch next character + CALL L2C88 ; routine ALPHANUM sets carry if not alphanum + JR NC,L293E ; forward to V-FOUND-1 with a full match. + +;; V-GET-PTR +L2929: POP HL ; pop saved pointer to char 1 + +;; V-NEXT +L292A: PUSH BC ; save flags + CALL L19B8 ; routine NEXT-ONE gets next variable in DE + EX DE,HL ; transfer to HL. + POP BC ; restore the flags + JR L2900 ; loop back to V-EACH + ; to compare each variable + +; --- + +;; V-80-BYTE +L2932: SET 7,B ; will signal not found + +; the branch was here when checking syntax + +;; V-SYNTAX +L2934: POP DE ; discard the pointer to 2nd. character v2 + ; in BASIC line/workspace. + + RST 18H ; GET-CHAR gets character after variable name. + CP $28 ; is it '(' ? + JR Z,L2943 ; forward to V-PASS + ; Note. could go straight to V-END ? + + SET 5,B ; signal not an array + JR L294B ; forward to V-END + +; --------------------------- + +; the jump was here when a long name matched and HL pointing to last character +; in variables area. + +;; V-FOUND-1 +L293E: POP DE ; discard pointer to first var letter + +; the jump was here with all other matches HL points to first var char. + +;; V-FOUND-2 +L293F: POP DE ; discard pointer to 2nd prog char v2 + POP DE ; drop pointer to 1st prog char v1 + PUSH HL ; save pointer to last char in vars + + RST 18H ; GET-CHAR + +;; V-PASS +L2943: CALL L2C88 ; routine ALPHANUM + JR NC,L294B ; forward to V-END if not + +; but it never will be as we advanced past long-named variables earlier. + + RST 20H ; NEXT-CHAR + JR L2943 ; back to V-PASS + +; --- + +;; V-END +L294B: POP HL ; pop the pointer to first character in + ; BASIC line/workspace. + RL B ; rotate the B register left + ; bit 7 to carry + BIT 6,B ; test the array indicator bit. + RET ; return + +; ----------------------- +; Stack function argument +; ----------------------- +; This branch is taken from LOOK-VARS when a defined function is currently +; being evaluated. +; Scanning is evaluating the expression after the '=' and the variable +; found could be in the argument list to the left of the '=' or in the +; normal place after the program. Preference will be given to the former. +; The variable name to be matched is in C. + +;; STK-F-ARG +L2951: LD HL,($5C0B) ; set HL to DEFADD + LD A,(HL) ; load the first character + CP $29 ; is it ')' ? + JP Z,L28EF ; JUMP back to V-RUN/SYN, if so, as there are + ; no arguments. + +; but proceed to search argument list of defined function first if not empty. + +;; SFA-LOOP +L295A: LD A,(HL) ; fetch character again. + OR $60 ; or with 01100000 presume a simple variable. + LD B,A ; save result in B. + INC HL ; address next location. + LD A,(HL) ; pick up byte. + CP $0E ; is it the number marker ? + JR Z,L296B ; forward to SFA-CP-VR if so. + +; it was a string. White-space may be present but syntax has been checked. + + DEC HL ; point back to letter. + CALL L28AB ; routine FN-SKPOVR skips to the '$' + INC HL ; now address the hidden marker. + RES 5,B ; signal a string variable. + +;; SFA-CP-VR +L296B: LD A,B ; transfer found variable letter to A. + CP C ; compare with expected. + JR Z,L2981 ; forward to SFA-MATCH with a match. + + INC HL ; step + INC HL ; past + INC HL ; the + INC HL ; five + INC HL ; bytes. + + CALL L28AB ; routine FN-SKPOVR skips to next character + CP $29 ; is it ')' ? + JP Z,L28EF ; jump back if so to V-RUN/SYN to look in + ; normal variables area. + + CALL L28AB ; routine FN-SKPOVR skips past the ',' + ; all syntax has been checked and these + ; things can be taken as read. + JR L295A ; back to SFA-LOOP while there are more + ; arguments. + +; --- + +;; SFA-MATCH +L2981: BIT 5,C ; test if numeric + JR NZ,L2991 ; to SFA-END if so as will be stacked + ; by scanning + + INC HL ; point to start of string descriptor + LD DE,($5C65) ; set DE to STKEND + CALL L33C0 ; routine MOVE-FP puts parameters on stack. + EX DE,HL ; new free location to HL. + LD ($5C65),HL ; use it to set STKEND system variable. + +;; SFA-END +L2991: POP DE ; discard + POP DE ; pointers. + XOR A ; clear carry flag. + INC A ; and zero flag. + RET ; return. + +; ------------------------ +; Stack variable component +; ------------------------ +; This is called to evaluate a complex structure that has been found, in +; runtime, by LOOK-VARS in the variables area. +; In this case HL points to the initial letter, bits 7-5 +; of which indicate the type of variable. +; 010 - simple string, 110 - string array, 100 - array of numbers. +; +; It is called from CLASS-01 when assigning to a string or array including +; a slice. +; It is called from SCANNING to isolate the required part of the structure. +; +; An important part of the runtime process is to check that the number of +; dimensions of the variable match the number of subscripts supplied in the +; BASIC line. +; +; If checking syntax, +; the B register, which counts dimensions is set to zero (256) to allow +; the loop to continue till all subscripts are checked. While doing this it +; is reading dimension sizes from some arbitrary area of memory. Although +; these are meaningless it is of no concern as the limit is never checked by +; int-exp during syntax checking. +; +; The routine is also called from the syntax path of DIM command to check the +; syntax of both string and numeric arrays definitions except that bit 6 of C +; is reset so both are checked as numeric arrays. This ruse avoids a terminal +; slice being accepted as part of the DIM command. +; All that is being checked is that there are a valid set of comma-separated +; expressions before a terminal ')', although, as above, it will still go +; through the motions of checking dummy dimension sizes. + +;; STK-VAR +L2996: XOR A ; clear A + LD B,A ; and B, the syntax dimension counter (256) + BIT 7,C ; checking syntax ? + JR NZ,L29E7 ; forward to SV-COUNT if so. + +; runtime evaluation. + + BIT 7,(HL) ; will be reset if a simple string. + JR NZ,L29AE ; forward to SV-ARRAYS otherwise + + INC A ; set A to 1, simple string. + +;; SV-SIMPLE$ +L29A1: INC HL ; address length low + LD C,(HL) ; place in C + INC HL ; address length high + LD B,(HL) ; place in B + INC HL ; address start of string + EX DE,HL ; DE = start now. + CALL L2AB2 ; routine STK-STO-$ stacks string parameters + ; DE start in variables area, + ; BC length, A=1 simple string + +; the only thing now is to consider if a slice is required. + + RST 18H ; GET-CHAR puts character at CH_ADD in A + JP L2A49 ; jump forward to SV-SLICE? to test for '(' + +; -------------------------------------------------------- + +; the branch was here with string and numeric arrays in runtime. + +;; SV-ARRAYS +L29AE: INC HL ; step past + INC HL ; the total length + INC HL ; to address Number of dimensions. + LD B,(HL) ; transfer to B overwriting zero. + BIT 6,C ; a numeric array ? + JR Z,L29C0 ; forward to SV-PTR with numeric arrays + + DEC B ; ignore the final element of a string array + ; the fixed string size. + + JR Z,L29A1 ; back to SV-SIMPLE$ if result is zero as has + ; been created with DIM a$(10) for instance + ; and can be treated as a simple string. + +; proceed with multi-dimensioned string arrays in runtime. + + EX DE,HL ; save pointer to dimensions in DE + + RST 18H ; GET-CHAR looks at the BASIC line + CP $28 ; is character '(' ? + JR NZ,L2A20 ; to REPORT-3 if not + ; 'Subscript wrong' + + EX DE,HL ; dimensions pointer to HL to synchronize + ; with next instruction. + +; runtime numeric arrays path rejoins here. + +;; SV-PTR +L29C0: EX DE,HL ; save dimension pointer in DE + JR L29E7 ; forward to SV-COUNT with true no of dims + ; in B. As there is no initial comma the + ; loop is entered at the midpoint. + +; ---------------------------------------------------------- +; the dimension counting loop which is entered at mid-point. + +;; SV-COMMA +L29C3: PUSH HL ; save counter + + RST 18H ; GET-CHAR + + POP HL ; pop counter + CP $2C ; is character ',' ? + JR Z,L29EA ; forward to SV-LOOP if so + +; in runtime the variable definition indicates a comma should appear here + + BIT 7,C ; checking syntax ? + JR Z,L2A20 ; forward to REPORT-3 if not + ; 'Subscript error' + +; proceed if checking syntax of an array? + + BIT 6,C ; array of strings + JR NZ,L29D8 ; forward to SV-CLOSE if so + +; an array of numbers. + + CP $29 ; is character ')' ? + JR NZ,L2A12 ; forward to SV-RPT-C if not + ; 'Nonsense in BASIC' + + RST 20H ; NEXT-CHAR moves CH-ADD past the statement + RET ; return -> + +; --- + +; the branch was here with an array of strings. + +;; SV-CLOSE +L29D8: CP $29 ; as above ')' could follow the expression + JR Z,L2A48 ; forward to SV-DIM if so + + CP $CC ; is it 'TO' ? + JR NZ,L2A12 ; to SV-RPT-C with anything else + ; 'Nonsense in BASIC' + +; now backtrack CH_ADD to set up for slicing routine. +; Note. in a BASIC line we can safely backtrack to a colour parameter. + +;; SV-CH-ADD +L29E0: RST 18H ; GET-CHAR + DEC HL ; backtrack HL + LD ($5C5D),HL ; to set CH_ADD up for slicing routine + JR L2A45 ; forward to SV-SLICE and make a return + ; when all slicing complete. + +; ---------------------------------------- +; -> the mid-point entry point of the loop + +;; SV-COUNT +L29E7: LD HL,$0000 ; initialize data pointer to zero. + +;; SV-LOOP +L29EA: PUSH HL ; save the data pointer. + + RST 20H ; NEXT-CHAR in BASIC area points to an + ; expression. + + POP HL ; restore the data pointer. + LD A,C ; transfer name/type to A. + CP $C0 ; is it 11000000 ? + ; Note. the letter component is absent if + ; syntax checking. + JR NZ,L29FB ; forward to SV-MULT if not an array of + ; strings. + +; proceed to check string arrays during syntax. + + RST 18H ; GET-CHAR + CP $29 ; ')' end of subscripts ? + JR Z,L2A48 ; forward to SV-DIM to consider further slice + + CP $CC ; is it 'TO' ? + JR Z,L29E0 ; back to SV-CH-ADD to consider a slice. + ; (no need to repeat get-char at L29E0) + +; if neither, then an expression is required so rejoin runtime loop ?? +; registers HL and DE only point to somewhere meaningful in runtime so +; comments apply to that situation. + +;; SV-MULT +L29FB: PUSH BC ; save dimension number. + PUSH HL ; push data pointer/rubbish. + ; DE points to current dimension. + CALL L2AEE ; routine DE,(DE+1) gets next dimension in DE + ; and HL points to it. + EX (SP),HL ; dim pointer to stack, data pointer to HL (*) + EX DE,HL ; data pointer to DE, dim size to HL. + + CALL L2ACC ; routine INT-EXP1 checks integer expression + ; and gets result in BC in runtime. + JR C,L2A20 ; to REPORT-3 if > HL + ; 'Subscript out of range' + + DEC BC ; adjust returned result from 1-x to 0-x + CALL L2AF4 ; routine GET-HL*DE multiplies data pointer by + ; dimension size. + ADD HL,BC ; add the integer returned by expression. + POP DE ; pop the dimension pointer. *** + POP BC ; pop dimension counter. + DJNZ L29C3 ; back to SV-COMMA if more dimensions + ; Note. during syntax checking, unless there + ; are more than 256 subscripts, the branch + ; back to SV-COMMA is always taken. + + BIT 7,C ; are we checking syntax ? + ; then we've got a joker here. + +;; SV-RPT-C +L2A12: JR NZ,L2A7A ; forward to SL-RPT-C if so + ; 'Nonsense in BASIC' + ; more than 256 subscripts in BASIC line. + +; but in runtime the number of subscripts are at least the same as dims + + PUSH HL ; save data pointer. + BIT 6,C ; is it a string array ? + JR NZ,L2A2C ; forward to SV-ELEM$ if so. + +; a runtime numeric array subscript. + + LD B,D ; register DE has advanced past all dimensions + LD C,E ; and points to start of data in variable. + ; transfer it to BC. + + RST 18H ; GET-CHAR checks BASIC line + CP $29 ; must be a ')' ? + JR Z,L2A22 ; skip to SV-NUMBER if so + +; else more subscripts in BASIC line than the variable definition. + +;; REPORT-3 +L2A20: RST 08H ; ERROR-1 + DEFB $02 ; Error Report: Subscript wrong + +; continue if subscripts matched the numeric array. + +;; SV-NUMBER +L2A22: RST 20H ; NEXT-CHAR moves CH_ADD to next statement + ; - finished parsing. + + POP HL ; pop the data pointer. + LD DE,$0005 ; each numeric element is 5 bytes. + CALL L2AF4 ; routine GET-HL*DE multiplies. + ADD HL,BC ; now add to start of data in the variable. + + RET ; return with HL pointing at the numeric + ; array subscript. -> + +; --------------------------------------------------------------- + +; the branch was here for string subscripts when the number of subscripts +; in the BASIC line was one less than in variable definition. + +;; SV-ELEM$ +L2A2C: CALL L2AEE ; routine DE,(DE+1) gets final dimension + ; the length of strings in this array. + EX (SP),HL ; start pointer to stack, data pointer to HL. + CALL L2AF4 ; routine GET-HL*DE multiplies by element + ; size. + POP BC ; the start of data pointer is added + ADD HL,BC ; in - now points to location before. + INC HL ; point to start of required string. + LD B,D ; transfer the length (final dimension size) + LD C,E ; from DE to BC. + EX DE,HL ; put start in DE. + CALL L2AB1 ; routine STK-ST-0 stores the string parameters + ; with A=0 - a slice or subscript. + +; now check that there were no more subscripts in the BASIC line. + + RST 18H ; GET-CHAR + CP $29 ; is it ')' ? + JR Z,L2A48 ; forward to SV-DIM to consider a separate + ; subscript or/and a slice. + + CP $2C ; a comma is allowed if the final subscript + ; is to be sliced e.g a$(2,3,4 TO 6). + JR NZ,L2A20 ; to REPORT-3 with anything else + ; 'Subscript error' + +;; SV-SLICE +L2A45: CALL L2A52 ; routine SLICING slices the string. + +; but a slice of a simple string can itself be sliced. + +;; SV-DIM +L2A48: RST 20H ; NEXT-CHAR + +;; SV-SLICE? +L2A49: CP $28 ; is character '(' ? + JR Z,L2A45 ; loop back if so to SV-SLICE + + RES 6,(IY+$01) ; update FLAGS - Signal string result + RET ; and return. + +; --- + +; The above section deals with the flexible syntax allowed. +; DIM a$(3,3,10) can be considered as two dimensional array of ten-character +; strings or a 3-dimensional array of characters. +; a$(1,1) will return a 10-character string as will a$(1,1,1 TO 10) +; a$(1,1,1) will return a single character. +; a$(1,1) (1 TO 6) is the same as a$(1,1,1 TO 6) +; A slice can itself be sliced ad infinitum +; b$ () () () () () () (2 TO 10) (2 TO 9) (3) is the same as b$(5) + + + +; ------------------------- +; Handle slicing of strings +; ------------------------- +; The syntax of string slicing is very natural and it is as well to reflect +; on the permutations possible. +; a$() and a$( TO ) indicate the entire string although just a$ would do +; and would avoid coming here. +; h$(16) indicates the single character at position 16. +; a$( TO 32) indicates the first 32 characters. +; a$(257 TO) indicates all except the first 256 characters. +; a$(19000 TO 19999) indicates the thousand characters at position 19000. +; Also a$(9 TO 5) returns a null string not an error. +; This enables a$(2 TO) to return a null string if the passed string is +; of length zero or 1. +; A string expression in brackets can be sliced. e.g. (STR$ PI) (3 TO ) +; We arrived here from SCANNING with CH-ADD pointing to the initial '(' +; or from above. + +;; SLICING +L2A52: CALL L2530 ; routine SYNTAX-Z + CALL NZ,L2BF1 ; routine STK-FETCH fetches parameters of + ; string at runtime, start in DE, length + ; in BC. This could be an array subscript. + + RST 20H ; NEXT-CHAR + CP $29 ; is it ')' ? e.g. a$() + JR Z,L2AAD ; forward to SL-STORE to store entire string. + + PUSH DE ; else save start address of string + + XOR A ; clear accumulator to use as a running flag. + PUSH AF ; and save on stack before any branching. + + PUSH BC ; save length of string to be sliced. + LD DE,$0001 ; default the start point to position 1. + + RST 18H ; GET-CHAR + + POP HL ; pop length to HL as default end point + ; and limit. + + CP $CC ; is it 'TO' ? e.g. a$( TO 10000) + JR Z,L2A81 ; to SL-SECOND to evaluate second parameter. + + POP AF ; pop the running flag. + + CALL L2ACD ; routine INT-EXP2 fetches first parameter. + + PUSH AF ; save flag (will be $FF if parameter>limit) + + LD D,B ; transfer the start + LD E,C ; to DE overwriting 0001. + PUSH HL ; save original length. + + RST 18H ; GET-CHAR + POP HL ; pop the limit length. + CP $CC ; is it 'TO' after a start ? + JR Z,L2A81 ; to SL-SECOND to evaluate second parameter + + CP $29 ; is it ')' ? e.g. a$(365) + +;; SL-RPT-C +L2A7A: JP NZ,L1C8A ; jump to REPORT-C with anything else + ; 'Nonsense in BASIC' + + LD H,D ; copy start + LD L,E ; to end - just a one character slice. + JR L2A94 ; forward to SL-DEFINE. + +; --------------------- + +;; SL-SECOND +L2A81: PUSH HL ; save limit length. + + RST 20H ; NEXT-CHAR + + POP HL ; pop the length. + + CP $29 ; is character ')' ? e.g a$(7 TO ) + JR Z,L2A94 ; to SL-DEFINE using length as end point. + + POP AF ; else restore flag. + CALL L2ACD ; routine INT-EXP2 gets second expression. + + PUSH AF ; save the running flag. + + RST 18H ; GET-CHAR + + LD H,B ; transfer second parameter + LD L,C ; to HL. e.g. a$(42 to 99) + CP $29 ; is character a ')' ? + JR NZ,L2A7A ; to SL-RPT-C if not + ; 'Nonsense in BASIC' + +; we now have start in DE and an end in HL. + +;; SL-DEFINE +L2A94: POP AF ; pop the running flag. + EX (SP),HL ; put end point on stack, start address to HL + ADD HL,DE ; add address of string to the start point. + DEC HL ; point to first character of slice. + EX (SP),HL ; start address to stack, end point to HL (*) + AND A ; prepare to subtract. + SBC HL,DE ; subtract start point from end point. + LD BC,$0000 ; default the length result to zero. + JR C,L2AA8 ; forward to SL-OVER if start > end. + + INC HL ; increment the length for inclusive byte. + + AND A ; now test the running flag. + JP M,L2A20 ; jump back to REPORT-3 if $FF. + ; 'Subscript out of range' + + LD B,H ; transfer the length + LD C,L ; to BC. + +;; SL-OVER +L2AA8: POP DE ; restore start address from machine stack *** + RES 6,(IY+$01) ; update FLAGS - signal string result for + ; syntax. + +;; SL-STORE +L2AAD: CALL L2530 ; routine SYNTAX-Z (UNSTACK-Z?) + RET Z ; return if checking syntax. + ; but continue to store the string in runtime. + +; ------------------------------------ +; other than from above, this routine is called from STK-VAR to stack +; a known string array element. +; ------------------------------------ + +;; STK-ST-0 +L2AB1: XOR A ; clear to signal a sliced string or element. + +; ------------------------- +; this routine is called from chr$, scrn$ etc. to store a simple string result. +; -------------------------- + +;; STK-STO-$ +L2AB2: RES 6,(IY+$01) ; update FLAGS - signal string result. + ; and continue to store parameters of string. + +; --------------------------------------- +; Pass five registers to calculator stack +; --------------------------------------- +; This subroutine puts five registers on the calculator stack. + +;; STK-STORE +L2AB6: PUSH BC ; save two registers + CALL L33A9 ; routine TEST-5-SP checks room and puts 5 + ; in BC. + POP BC ; fetch the saved registers. + LD HL,($5C65) ; make HL point to first empty location STKEND + LD (HL),A ; place the 5 registers. + INC HL ; + LD (HL),E ; + INC HL ; + LD (HL),D ; + INC HL ; + LD (HL),C ; + INC HL ; + LD (HL),B ; + INC HL ; + LD ($5C65),HL ; update system variable STKEND. + RET ; and return. + +; ------------------------------------------- +; Return result of evaluating next expression +; ------------------------------------------- +; This clever routine is used to check and evaluate an integer expression +; which is returned in BC, setting A to $FF, if greater than a limit supplied +; in HL. It is used to check array subscripts, parameters of a string slice +; and the arguments of the DIM command. In the latter case, the limit check +; is not required and H is set to $FF. When checking optional string slice +; parameters, it is entered at the second entry point so as not to disturb +; the running flag A, which may be $00 or $FF from a previous invocation. + +;; INT-EXP1 +L2ACC: XOR A ; set result flag to zero. + +; -> The entry point is here if A is used as a running flag. + +;; INT-EXP2 +L2ACD: PUSH DE ; preserve DE register throughout. + PUSH HL ; save the supplied limit. + PUSH AF ; save the flag. + + CALL L1C82 ; routine EXPT-1NUM evaluates expression + ; at CH_ADD returning if numeric result, + ; with value on calculator stack. + + POP AF ; pop the flag. + CALL L2530 ; routine SYNTAX-Z + JR Z,L2AEB ; forward to I-RESTORE if checking syntax so + ; avoiding a comparison with supplied limit. + + PUSH AF ; save the flag. + + CALL L1E99 ; routine FIND-INT2 fetches value from + ; calculator stack to BC producing an error + ; if too high. + + POP DE ; pop the flag to D. + LD A,B ; test value for zero and reject + OR C ; as arrays and strings begin at 1. + SCF ; set carry flag. + JR Z,L2AE8 ; forward to I-CARRY if zero. + + POP HL ; restore the limit. + PUSH HL ; and save. + AND A ; prepare to subtract. + SBC HL,BC ; subtract value from limit. + +;; I-CARRY +L2AE8: LD A,D ; move flag to accumulator $00 or $FF. + SBC A,$00 ; will set to $FF if carry set. + +;; I-RESTORE +L2AEB: POP HL ; restore the limit. + POP DE ; and DE register. + RET ; return. + + +; ----------------------- +; LD DE,(DE+1) Subroutine +; ----------------------- +; This routine just loads the DE register with the contents of the two +; locations following the location addressed by DE. +; It is used to step along the 16-bit dimension sizes in array definitions. +; Note. Such code is made into subroutines to make programs easier to +; write and it would use less space to include the five instructions in-line. +; However, there are so many exchanges going on at the places this is invoked +; that to implement it in-line would make the code hard to follow. +; It probably had a zippier label though as the intention is to simplify the +; program. + +;; DE,(DE+1) +L2AEE: EX DE,HL ; + INC HL ; + LD E,(HL) ; + INC HL ; + LD D,(HL) ; + RET ; + +; ------------------- +; HL=HL*DE Subroutine +; ------------------- +; This routine calls the mathematical routine to multiply HL by DE in runtime. +; It is called from STK-VAR and from DIM. In the latter case syntax is not +; being checked so the entry point could have been at the second CALL +; instruction to save a few clock-cycles. + +;; GET-HL*DE +L2AF4: CALL L2530 ; routine SYNTAX-Z. + RET Z ; return if checking syntax. + + CALL L30A9 ; routine HL-HL*DE. + JP C,L1F15 ; jump back to REPORT-4 if over 65535. + + RET ; else return with 16-bit result in HL. + +; ----------------- +; THE 'LET' COMMAND +; ----------------- +; Sinclair BASIC adheres to the ANSI-78 standard and a LET is required in +; assignments e.g. LET a = 1 : LET h$ = "hat". +; +; Long names may contain spaces but not colour controls (when assigned). +; a substring can appear to the left of the equals sign. + +; An earlier mathematician Lewis Carroll may have been pleased that +; 10 LET Babies cannot manage crocodiles = Babies are illogical AND +; Nobody is despised who can manage a crocodile AND Illogical persons +; are despised +; does not give the 'Nonsense..' error if the three variables exist. +; I digress. + +;; LET +L2AFF: LD HL,($5C4D) ; fetch system variable DEST to HL. + BIT 1,(IY+$37) ; test FLAGX - handling a new variable ? + JR Z,L2B66 ; forward to L-EXISTS if not. + +; continue for a new variable. DEST points to start in BASIC line. +; from the CLASS routines. + + LD BC,$0005 ; assume numeric and assign an initial 5 bytes + +;; L-EACH-CH +L2B0B: INC BC ; increase byte count for each relevant + ; character + +;; L-NO-SP +L2B0C: INC HL ; increase pointer. + LD A,(HL) ; fetch character. + CP $20 ; is it a space ? + JR Z,L2B0C ; back to L-NO-SP is so. + + JR NC,L2B1F ; forward to L-TEST-CH if higher. + + CP $10 ; is it $00 - $0F ? + JR C,L2B29 ; forward to L-SPACES if so. + + CP $16 ; is it $16 - $1F ? + JR NC,L2B29 ; forward to L-SPACES if so. + +; it was $10 - $15 so step over a colour code. + + INC HL ; increase pointer. + JR L2B0C ; loop back to L-NO-SP. + +; --- + +; the branch was to here if higher than space. + +;; L-TEST-CH +L2B1F: CALL L2C88 ; routine ALPHANUM sets carry if alphanumeric + JR C,L2B0B ; loop back to L-EACH-CH for more if so. + + CP $24 ; is it '$' ? + JP Z,L2BC0 ; jump forward if so, to L-NEW$ + ; with a new string. + +;; L-SPACES +L2B29: LD A,C ; save length lo in A. + LD HL,($5C59) ; fetch E_LINE to HL. + DEC HL ; point to location before, the variables + ; end-marker. + CALL L1655 ; routine MAKE-ROOM creates BC spaces + ; for name and numeric value. + INC HL ; advance to first new location. + INC HL ; then to second. + EX DE,HL ; set DE to second location. + PUSH DE ; save this pointer. + LD HL,($5C4D) ; reload HL with DEST. + DEC DE ; point to first. + SUB $06 ; subtract six from length_lo. + LD B,A ; save count in B. + JR Z,L2B4F ; forward to L-SINGLE if it was just + ; one character. + +; HL points to start of variable name after 'LET' in BASIC line. + +;; L-CHAR +L2B3E: INC HL ; increase pointer. + LD A,(HL) ; pick up character. + CP $21 ; is it space or higher ? + JR C,L2B3E ; back to L-CHAR with space and less. + + OR $20 ; make variable lower-case. + INC DE ; increase destination pointer. + LD (DE),A ; and load to edit line. + DJNZ L2B3E ; loop back to L-CHAR until B is zero. + + OR $80 ; invert the last character. + LD (DE),A ; and overwrite that in edit line. + +; now consider first character which has bit 6 set + + LD A,$C0 ; set A 11000000 is xor mask for a long name. + ; %101 is xor/or result + +; single character numerics rejoin here with %00000000 in mask. +; %011 will be xor/or result + +;; L-SINGLE +L2B4F: LD HL,($5C4D) ; fetch DEST - HL addresses first character. + XOR (HL) ; apply variable type indicator mask (above). + OR $20 ; make lowercase - set bit 5. + POP HL ; restore pointer to 2nd character. + CALL L2BEA ; routine L-FIRST puts A in first character. + ; and returns with HL holding + ; new E_LINE-1 the $80 vars end-marker. + +;; L-NUMERIC +L2B59: PUSH HL ; save the pointer. + +; the value of variable is deleted but remains after calculator stack. + + RST 28H ;; FP-CALC + DEFB $02 ;;delete ; delete variable value + DEFB $38 ;;end-calc + +; DE (STKEND) points to start of value. + + POP HL ; restore the pointer. + LD BC,$0005 ; start of number is five bytes before. + AND A ; prepare for true subtraction. + SBC HL,BC ; HL points to start of value. + JR L2BA6 ; forward to L-ENTER ==> + +; --- + + +; the jump was to here if the variable already existed. + +;; L-EXISTS +L2B66: BIT 6,(IY+$01) ; test FLAGS - numeric or string result ? + JR Z,L2B72 ; skip forward to L-DELETE$ -*-> + ; if string result. + +; A numeric variable could be simple or an array element. +; They are treated the same and the old value is overwritten. + + LD DE,$0006 ; six bytes forward points to loc past value. + ADD HL,DE ; add to start of number. + JR L2B59 ; back to L-NUMERIC to overwrite value. + +; --- + +; -*-> the branch was here if a string existed. + +;; L-DELETE$ +L2B72: LD HL,($5C4D) ; fetch DEST to HL. + ; (still set from first instruction) + LD BC,($5C72) ; fetch STRLEN to BC. + BIT 0,(IY+$37) ; test FLAGX - handling a complete simple + ; string ? + JR NZ,L2BAF ; forward to L-ADD$ if so. + +; must be a string array or a slice in workspace. +; Note. LET a$(3 TO 6) = h$ will assign "hat " if h$ = "hat" +; and "hats" if h$ = "hatstand". +; +; This is known as Procrustian lengthening and shortening after a +; character Procrustes in Greek legend who made travellers sleep in his bed, +; cutting off their feet or stretching them so they fitted the bed perfectly. +; The bloke was hatstand and slain by Theseus. + + LD A,B ; test if length + OR C ; is zero and + RET Z ; return if so. + + PUSH HL ; save pointer to start. + + RST 30H ; BC-SPACES creates room. + PUSH DE ; save pointer to first new location. + PUSH BC ; and length (*) + LD D,H ; set DE to point to last location. + LD E,L ; + INC HL ; set HL to next location. + LD (HL),$20 ; place a space there. + LDDR ; copy bytes filling with spaces. + + PUSH HL ; save pointer to start. + CALL L2BF1 ; routine STK-FETCH start to DE, + ; length to BC. + POP HL ; restore the pointer. + EX (SP),HL ; (*) length to HL, pointer to stack. + AND A ; prepare for true subtraction. + SBC HL,BC ; subtract old length from new. + ADD HL,BC ; and add back. + JR NC,L2B9B ; forward if it fits to L-LENGTH. + + LD B,H ; otherwise set + LD C,L ; length to old length. + ; "hatstand" becomes "hats" + +;; L-LENGTH +L2B9B: EX (SP),HL ; (*) length to stack, pointer to HL. + EX DE,HL ; pointer to DE, start of string to HL. + LD A,B ; is the length zero ? + OR C ; + JR Z,L2BA3 ; forward to L-IN-W/S if so + ; leaving prepared spaces. + + LDIR ; else copy bytes overwriting some spaces. + +;; L-IN-W/S +L2BA3: POP BC ; pop the new length. (*) + POP DE ; pop pointer to new area. + POP HL ; pop pointer to variable in assignment. + ; and continue copying from workspace + ; to variables area. + +; ==> branch here from L-NUMERIC + +;; L-ENTER +L2BA6: EX DE,HL ; exchange pointers HL=STKEND DE=end of vars. + LD A,B ; test the length + OR C ; and make a + RET Z ; return if zero (strings only). + + PUSH DE ; save start of destination. + LDIR ; copy bytes. + POP HL ; address the start. + RET ; and return. + +; --- + +; the branch was here from L-DELETE$ if an existing simple string. +; register HL addresses start of string in variables area. + +;; L-ADD$ +L2BAF: DEC HL ; point to high byte of length. + DEC HL ; to low byte. + DEC HL ; to letter. + LD A,(HL) ; fetch masked letter to A. + PUSH HL ; save the pointer on stack. + PUSH BC ; save new length. + CALL L2BC6 ; routine L-STRING adds new string at end + ; of variables area. + ; if no room we still have old one. + POP BC ; restore length. + POP HL ; restore start. + INC BC ; increase + INC BC ; length by three + INC BC ; to include character and length bytes. + JP L19E8 ; jump to indirect exit via RECLAIM-2 + ; deleting old version and adjusting pointers. + +; --- + +; the jump was here with a new string variable. + +;; L-NEW$ +L2BC0: LD A,$DF ; indicator mask %11011111 for + ; %010xxxxx will be result + LD HL,($5C4D) ; address DEST first character. + AND (HL) ; combine mask with character. + +;; L-STRING +L2BC6: PUSH AF ; save first character and mask. + CALL L2BF1 ; routine STK-FETCH fetches parameters of + ; the string. + EX DE,HL ; transfer start to HL. + ADD HL,BC ; add to length. + PUSH BC ; save the length. + DEC HL ; point to end of string. + LD ($5C4D),HL ; save pointer in DEST. + ; (updated by POINTERS if in workspace) + INC BC ; extra byte for letter. + INC BC ; two bytes + INC BC ; for the length of string. + LD HL,($5C59) ; address E_LINE. + DEC HL ; now end of VARS area. + CALL L1655 ; routine MAKE-ROOM makes room for string. + ; updating pointers including DEST. + LD HL,($5C4D) ; pick up pointer to end of string from DEST. + POP BC ; restore length from stack. + PUSH BC ; and save again on stack. + INC BC ; add a byte. + LDDR ; copy bytes from end to start. + EX DE,HL ; HL addresses length low + INC HL ; increase to address high byte + POP BC ; restore length to BC + LD (HL),B ; insert high byte + DEC HL ; address low byte location + LD (HL),C ; insert that byte + POP AF ; restore character and mask + +;; L-FIRST +L2BEA: DEC HL ; address variable name + LD (HL),A ; and insert character. + LD HL,($5C59) ; load HL with E_LINE. + DEC HL ; now end of VARS area. + RET ; return + +; ------------------------------------ +; Get last value from calculator stack +; ------------------------------------ +; +; + +;; STK-FETCH +L2BF1: LD HL,($5C65) ; STKEND + DEC HL ; + LD B,(HL) ; + DEC HL ; + LD C,(HL) ; + DEC HL ; + LD D,(HL) ; + DEC HL ; + LD E,(HL) ; + DEC HL ; + LD A,(HL) ; + LD ($5C65),HL ; STKEND + RET ; + +; ------------------ +; Handle DIM command +; ------------------ +; e.g. DIM a(2,3,4,7): DIM a$(32) : DIM b$(300,2,768) : DIM c$(20000) +; the only limit to dimensions is memory so, for example, +; DIM a(2,2,2,2,2,2,2,2,2,2,2,2,2) is possible and creates a multi- +; dimensional array of zeros. String arrays are initialized to spaces. +; It is not possible to erase an array, but it can be re-dimensioned to +; a minimal size of 1, after use, to free up memory. + +;; DIM +L2C02: CALL L28B2 ; routine LOOK-VARS + +;; D-RPORT-C +L2C05: JP NZ,L1C8A ; jump to REPORT-C if a long-name variable. + ; DIM lottery numbers(49) doesn't work. + + CALL L2530 ; routine SYNTAX-Z + JR NZ,L2C15 ; forward to D-RUN in runtime. + + RES 6,C ; signal 'numeric' array even if string as + ; this simplifies the syntax checking. + + CALL L2996 ; routine STK-VAR checks syntax. + CALL L1BEE ; routine CHECK-END performs early exit -> + +; the branch was here in runtime. + +;; D-RUN +L2C15: JR C,L2C1F ; skip to D-LETTER if variable did not exist. + ; else reclaim the old one. + + PUSH BC ; save type in C. + CALL L19B8 ; routine NEXT-ONE find following variable + ; or position of $80 end-marker. + CALL L19E8 ; routine RECLAIM-2 reclaims the + ; space between. + POP BC ; pop the type. + +;; D-LETTER +L2C1F: SET 7,C ; signal array. + LD B,$00 ; initialize dimensions to zero and + PUSH BC ; save with the type. + LD HL,$0001 ; make elements one character presuming string + BIT 6,C ; is it a string ? + JR NZ,L2C2D ; forward to D-SIZE if so. + + LD L,$05 ; make elements 5 bytes as is numeric. + +;; D-SIZE +L2C2D: EX DE,HL ; save the element size in DE. + +; now enter a loop to parse each of the integers in the list. + +;; D-NO-LOOP +L2C2E: RST 20H ; NEXT-CHAR + LD H,$FF ; disable limit check by setting HL high + CALL L2ACC ; routine INT-EXP1 + JP C,L2A20 ; to REPORT-3 if > 65280 and then some + ; 'Subscript out of range' + + POP HL ; pop dimension counter, array type + PUSH BC ; save dimension size *** + INC H ; increment the dimension counter + PUSH HL ; save the dimension counter + LD H,B ; transfer size + LD L,C ; to HL + CALL L2AF4 ; routine GET-HL*DE multiplies dimension by + ; running total of size required initially + ; 1 or 5. + EX DE,HL ; save running total in DE + + RST 18H ; GET-CHAR + CP $2C ; is it ',' ? + JR Z,L2C2E ; loop back to D-NO-LOOP until all dimensions + ; have been considered + +; when loop complete continue. + + CP $29 ; is it ')' ? + JR NZ,L2C05 ; to D-RPORT-C with anything else + ; 'Nonsense in BASIC' + + + RST 20H ; NEXT-CHAR advances to next statement/CR + + POP BC ; pop dimension counter/type + LD A,C ; type to A + +; now calculate space required for array variable + + LD L,B ; dimensions to L since these require 16 bits + ; then this value will be doubled + LD H,$00 ; set high byte to zero + +; another four bytes are required for letter(1), total length(2), number of +; dimensions(1) but since we have yet to double allow for two + + INC HL ; increment + INC HL ; increment + + ADD HL,HL ; now double giving 4 + dimensions * 2 + + ADD HL,DE ; add to space required for array contents + + JP C,L1F15 ; to REPORT-4 if > 65535 + ; 'Out of memory' + + PUSH DE ; save data space + PUSH BC ; save dimensions/type + PUSH HL ; save total space + LD B,H ; total space + LD C,L ; to BC + LD HL,($5C59) ; address E_LINE - first location after + ; variables area + DEC HL ; point to location before - the $80 end-marker + CALL L1655 ; routine MAKE-ROOM creates the space if + ; memory is available. + + INC HL ; point to first new location and + LD (HL),A ; store letter/type + + POP BC ; pop total space + DEC BC ; exclude name + DEC BC ; exclude the 16-bit + DEC BC ; counter itself + INC HL ; point to next location the 16-bit counter + LD (HL),C ; insert low byte + INC HL ; address next + LD (HL),B ; insert high byte + + POP BC ; pop the number of dimensions. + LD A,B ; dimensions to A + INC HL ; address next + LD (HL),A ; and insert "No. of dims" + + LD H,D ; transfer DE space + 1 from make-room + LD L,E ; to HL + DEC DE ; set DE to next location down. + LD (HL),$00 ; presume numeric and insert a zero + BIT 6,C ; test bit 6 of C. numeric or string ? + JR Z,L2C7C ; skip to DIM-CLEAR if numeric + + LD (HL),$20 ; place a space character in HL + +;; DIM-CLEAR +L2C7C: POP BC ; pop the data length + + LDDR ; LDDR sets to zeros or spaces + +; The number of dimensions is still in A. +; A loop is now entered to insert the size of each dimension that was pushed +; during the D-NO-LOOP working downwards from position before start of data. + +;; DIM-SIZES +L2C7F: POP BC ; pop a dimension size *** + LD (HL),B ; insert high byte at position + DEC HL ; next location down + LD (HL),C ; insert low byte + DEC HL ; next location down + DEC A ; decrement dimension counter + JR NZ,L2C7F ; back to DIM-SIZES until all done. + + RET ; return. + +; ----------------------------- +; Check whether digit or letter +; ----------------------------- +; This routine checks that the character in A is alphanumeric +; returning with carry set if so. + +;; ALPHANUM +L2C88: CALL L2D1B ; routine NUMERIC will reset carry if so. + CCF ; Complement Carry Flag + RET C ; Return if numeric else continue into + ; next routine. + +; This routine checks that the character in A is alphabetic + +;; ALPHA +L2C8D: CP $41 ; less than 'A' ? + CCF ; Complement Carry Flag + RET NC ; return if so + + CP $5B ; less than 'Z'+1 ? + RET C ; is within first range + + CP $61 ; less than 'a' ? + CCF ; Complement Carry Flag + RET NC ; return if so. + + CP $7B ; less than 'z'+1 ? + RET ; carry set if within a-z. + +; ------------------------- +; Decimal to floating point +; ------------------------- +; This routine finds the floating point number represented by an expression +; beginning with BIN, '.' or a digit. +; Note that BIN need not have any '0's or '1's after it. +; BIN is really just a notational symbol and not a function. + +;; DEC-TO-FP +L2C9B: CP $C4 ; 'BIN' token ? + JR NZ,L2CB8 ; to NOT-BIN if not + + LD DE,$0000 ; initialize 16 bit buffer register. + +;; BIN-DIGIT +L2CA2: RST 20H ; NEXT-CHAR + SUB $31 ; '1' + ADC A,$00 ; will be zero if '1' or '0' + ; carry will be set if was '0' + JR NZ,L2CB3 ; forward to BIN-END if result not zero + + EX DE,HL ; buffer to HL + CCF ; Carry now set if originally '1' + ADC HL,HL ; shift the carry into HL + JP C,L31AD ; to REPORT-6 if overflow - too many digits + ; after first '1'. There can be an unlimited + ; number of leading zeros. + ; 'Number too big' - raise an error + + EX DE,HL ; save the buffer + JR L2CA2 ; back to BIN-DIGIT for more digits + +; --- + +;; BIN-END +L2CB3: LD B,D ; transfer 16 bit buffer + LD C,E ; to BC register pair. + JP L2D2B ; JUMP to STACK-BC to put on calculator stack + +; --- + +; continue here with .1, 42, 3.14, 5., 2.3 E -4 + +;; NOT-BIN +L2CB8: CP $2E ; '.' - leading decimal point ? + JR Z,L2CCB ; skip to DECIMAL if so. + + CALL L2D3B ; routine INT-TO-FP to evaluate all digits + ; This number 'x' is placed on stack. + CP $2E ; '.' - mid decimal point ? + + JR NZ,L2CEB ; to E-FORMAT if not to consider that format + + RST 20H ; NEXT-CHAR + CALL L2D1B ; routine NUMERIC returns carry reset if 0-9 + + JR C,L2CEB ; to E-FORMAT if not a digit e.g. '1.' + + JR L2CD5 ; to DEC-STO-1 to add the decimal part to 'x' + +; --- + +; a leading decimal point has been found in a number. + +;; DECIMAL +L2CCB: RST 20H ; NEXT-CHAR + CALL L2D1B ; routine NUMERIC will reset carry if digit + +;; DEC-RPT-C +L2CCF: JP C,L1C8A ; to REPORT-C if just a '.' + ; raise 'Nonsense in BASIC' + +; since there is no leading zero put one on the calculator stack. + + RST 28H ;; FP-CALC + DEFB $A0 ;;stk-zero ; 0. + DEFB $38 ;;end-calc + +; If rejoining from earlier there will be a value 'x' on stack. +; If continuing from above the value zero. +; Now store 1 in mem-0. +; Note. At each pass of the digit loop this will be divided by ten. + +;; DEC-STO-1 +L2CD5: RST 28H ;; FP-CALC + DEFB $A1 ;;stk-one ;x or 0,1. + DEFB $C0 ;;st-mem-0 ;x or 0,1. + DEFB $02 ;;delete ;x or 0. + DEFB $38 ;;end-calc + + +;; NXT-DGT-1 +L2CDA: RST 18H ; GET-CHAR + CALL L2D22 ; routine STK-DIGIT stacks single digit 'd' + JR C,L2CEB ; exit to E-FORMAT when digits exhausted > + + + RST 28H ;; FP-CALC ;x or 0,d. first pass. + DEFB $E0 ;;get-mem-0 ;x or 0,d,1. + DEFB $A4 ;;stk-ten ;x or 0,d,1,10. + DEFB $05 ;;division ;x or 0,d,1/10. + DEFB $C0 ;;st-mem-0 ;x or 0,d,1/10. + DEFB $04 ;;multiply ;x or 0,d/10. + DEFB $0F ;;addition ;x or 0 + d/10. + DEFB $38 ;;end-calc last value. + + RST 20H ; NEXT-CHAR moves to next character + JR L2CDA ; back to NXT-DGT-1 + +; --- + +; although only the first pass is shown it can be seen that at each pass +; the new less significant digit is multiplied by an increasingly smaller +; factor (1/100, 1/1000, 1/10000 ... ) before being added to the previous +; last value to form a new last value. + +; Finally see if an exponent has been input. + +;; E-FORMAT +L2CEB: CP $45 ; is character 'E' ? + JR Z,L2CF2 ; to SIGN-FLAG if so + + CP $65 ; 'e' is acceptable as well. + RET NZ ; return as no exponent. + +;; SIGN-FLAG +L2CF2: LD B,$FF ; initialize temporary sign byte to $FF + + RST 20H ; NEXT-CHAR + CP $2B ; is character '+' ? + JR Z,L2CFE ; to SIGN-DONE + + CP $2D ; is character '-' ? + JR NZ,L2CFF ; to ST-E-PART as no sign + + INC B ; set sign to zero + +; now consider digits of exponent. +; Note. incidentally this is the only occasion in Spectrum BASIC when an +; expression may not be used when a number is expected. + +;; SIGN-DONE +L2CFE: RST 20H ; NEXT-CHAR + +;; ST-E-PART +L2CFF: CALL L2D1B ; routine NUMERIC + JR C,L2CCF ; to DEC-RPT-C if not + ; raise 'Nonsense in BASIC'. + + PUSH BC ; save sign (in B) + CALL L2D3B ; routine INT-TO-FP places exponent on stack + CALL L2DD5 ; routine FP-TO-A transfers it to A + POP BC ; restore sign + JP C,L31AD ; to REPORT-6 if overflow (over 255) + ; raise 'Number too big'. + + AND A ; set flags + JP M,L31AD ; to REPORT-6 if over '127'. + ; raise 'Number too big'. + ; 127 is still way too high and it is + ; impossible to enter an exponent greater + ; than 39 from the keyboard. The error gets + ; raised later in E-TO-FP so two different + ; error messages depending how high A is. + + INC B ; $FF to $00 or $00 to $01 - expendable now. + JR Z,L2D18 ; forward to E-FP-JUMP if exponent positive + + NEG ; Negate the exponent. + +;; E-FP-JUMP +L2D18: JP L2D4F ; JUMP forward to E-TO-FP to assign to + ; last value x on stack x * 10 to power A + ; a relative jump would have done. + +; --------------------- +; Check for valid digit +; --------------------- +; This routine checks that the ASCII character in A is numeric +; returning with carry reset if so. + +;; NUMERIC +L2D1B: CP $30 ; '0' + RET C ; return if less than zero character. + + CP $3A ; The upper test is '9' + CCF ; Complement Carry Flag + RET ; Return - carry clear if character '0' - '9' + +; ----------- +; Stack Digit +; ----------- +; This subroutine is called from INT-TO-FP and DEC-TO-FP to stack a digit +; on the calculator stack. + +;; STK-DIGIT +L2D22: CALL L2D1B ; routine NUMERIC + RET C ; return if not numeric character + + SUB $30 ; convert from ASCII to digit + +; ----------------- +; Stack accumulator +; ----------------- +; +; + +;; STACK-A +L2D28: LD C,A ; transfer to C + LD B,$00 ; and make B zero + +; ---------------------- +; Stack BC register pair +; ---------------------- +; + +;; STACK-BC +L2D2B: LD IY,$5C3A ; re-initialize ERR_NR + + XOR A ; clear to signal small integer + LD E,A ; place in E for sign + LD D,C ; LSB to D + LD C,B ; MSB to C + LD B,A ; last byte not used + CALL L2AB6 ; routine STK-STORE + + RST 28H ;; FP-CALC + DEFB $38 ;;end-calc make HL = STKEND-5 + + AND A ; clear carry + RET ; before returning + +; ------------------------- +; Integer to floating point +; ------------------------- +; This routine places one or more digits found in a BASIC line +; on the calculator stack multiplying the previous value by ten each time +; before adding in the new digit to form a last value on calculator stack. + +;; INT-TO-FP +L2D3B: PUSH AF ; save first character + + RST 28H ;; FP-CALC + DEFB $A0 ;;stk-zero ; v=0. initial value + DEFB $38 ;;end-calc + + POP AF ; fetch first character back. + +;; NXT-DGT-2 +L2D40: CALL L2D22 ; routine STK-DIGIT puts 0-9 on stack + RET C ; will return when character is not numeric > + + RST 28H ;; FP-CALC ; v, d. + DEFB $01 ;;exchange ; d, v. + DEFB $A4 ;;stk-ten ; d, v, 10. + DEFB $04 ;;multiply ; d, v*10. + DEFB $0F ;;addition ; d + v*10 = newvalue + DEFB $38 ;;end-calc ; v. + + CALL L0074 ; routine CH-ADD+1 get next character + JR L2D40 ; back to NXT-DGT-2 to process as a digit + + +;********************************* +;** Part 9. ARITHMETIC ROUTINES ** +;********************************* + +; -------------------------- +; E-format to floating point +; -------------------------- +; This subroutine is used by the PRINT-FP routine and the decimal to FP +; routines to stack a number expressed in exponent format. +; Note. Though not used by the ROM as such, it has also been set up as +; a unary calculator literal but this will not work as the accumulator +; is not available from within the calculator. + +; on entry there is a value x on the calculator stack and an exponent of ten +; in A. The required value is x + 10 ^ A + +;; e-to-fp +;; E-TO-FP +L2D4F: RLCA ; this will set the x. + RRCA ; carry if bit 7 is set + + JR NC,L2D55 ; to E-SAVE if positive. + + CPL ; make negative positive + INC A ; without altering carry. + +;; E-SAVE +L2D55: PUSH AF ; save positive exp and sign in carry + + LD HL,$5C92 ; address MEM-0 + + CALL L350B ; routine FP-0/1 + ; places an integer zero, if no carry, + ; else a one in mem-0 as a sign flag + + RST 28H ;; FP-CALC + DEFB $A4 ;;stk-ten x, 10. + DEFB $38 ;;end-calc + + POP AF ; pop the exponent. + +; now enter a loop + +;; E-LOOP +L2D60: SRL A ; 0>76543210>C + + JR NC,L2D71 ; forward to E-TST-END if no bit + + PUSH AF ; save shifted exponent. + + RST 28H ;; FP-CALC + DEFB $C1 ;;st-mem-1 x, 10. + DEFB $E0 ;;get-mem-0 x, 10, (0/1). + DEFB $00 ;;jump-true + + DEFB $04 ;;to L2D6D, E-DIVSN + + DEFB $04 ;;multiply x*10. + DEFB $33 ;;jump + + DEFB $02 ;;to L2D6E, E-FETCH + +;; E-DIVSN +L2D6D: DEFB $05 ;;division x/10. + +;; E-FETCH +L2D6E: DEFB $E1 ;;get-mem-1 x/10 or x*10, 10. + DEFB $38 ;;end-calc new x, 10. + + POP AF ; restore shifted exponent + +; the loop branched to here with no carry + +;; E-TST-END +L2D71: JR Z,L2D7B ; forward to E-END if A emptied of bits + + PUSH AF ; re-save shifted exponent + + RST 28H ;; FP-CALC + DEFB $31 ;;duplicate new x, 10, 10. + DEFB $04 ;;multiply new x, 100. + DEFB $38 ;;end-calc + + POP AF ; restore shifted exponent + JR L2D60 ; back to E-LOOP until all bits done. + +; --- + +; although only the first pass is shown it can be seen that for each set bit +; representing a power of two, x is multiplied or divided by the +; corresponding power of ten. + +;; E-END +L2D7B: RST 28H ;; FP-CALC final x, factor. + DEFB $02 ;;delete final x. + DEFB $38 ;;end-calc x. + + RET ; return + + + + +; ------------- +; Fetch integer +; ------------- +; This routine is called by the mathematical routines - FP-TO-BC, PRINT-FP, +; mult, re-stack and negate to fetch an integer from address HL. +; HL points to the stack or a location in MEM and no deletion occurs. +; If the number is negative then a similar process to that used in INT-STORE +; is used to restore the twos complement number to normal in DE and a sign +; in C. + +;; INT-FETCH +L2D7F: INC HL ; skip zero indicator. + LD C,(HL) ; fetch sign to C + INC HL ; address low byte + LD A,(HL) ; fetch to A + XOR C ; two's complement + SUB C ; + LD E,A ; place in E + INC HL ; address high byte + LD A,(HL) ; fetch to A + ADC A,C ; two's complement + XOR C ; + LD D,A ; place in D + RET ; return + +; ------------------------ +; Store a positive integer +; ------------------------ +; This entry point is not used in this ROM but would +; store any integer as positive. + +;; p-int-sto +L2D8C: LD C,$00 ; make sign byte positive and continue + +; ------------- +; Store integer +; ------------- +; this routine stores an integer in DE at address HL. +; It is called from mult, truncate, negate and sgn. +; The sign byte $00 +ve or $FF -ve is in C. +; If negative, the number is stored in 2's complement form so that it is +; ready to be added. + +;; INT-STORE +L2D8E: PUSH HL ; preserve HL + + LD (HL),$00 ; first byte zero shows integer not exponent + INC HL ; + LD (HL),C ; then store the sign byte + INC HL ; + ; e.g. +1 -1 + LD A,E ; fetch low byte 00000001 00000001 + XOR C ; xor sign 00000000 or 11111111 + ; gives 00000001 or 11111110 + SUB C ; sub sign 00000000 or 11111111 + ; gives 00000001>0 or 11111111>C + LD (HL),A ; store 2's complement. + INC HL ; + LD A,D ; high byte 00000000 00000000 + ADC A,C ; sign 00000000<0 11111111 65535, overflow + + PUSH AF ; save the value and flags + DEC B ; and test that + INC B ; the high byte is zero. + JR Z,L2DE1 ; forward FP-A-END if zero + +; else there has been 8-bit overflow + + POP AF ; retrieve the value + SCF ; set carry flag to show overflow + RET ; and return. + +; --- + +;; FP-A-END +L2DE1: POP AF ; restore value and success flag and + RET ; return. + + +; ----------------------------- +; Print a floating point number +; ----------------------------- +; Not a trivial task. +; Begin by considering whether to print a leading sign for negative numbers. + +;; PRINT-FP +L2DE3: RST 28H ;; FP-CALC + DEFB $31 ;;duplicate + DEFB $36 ;;less-0 + DEFB $00 ;;jump-true + + DEFB $0B ;;to L2DF2, PF-NEGTVE + + DEFB $31 ;;duplicate + DEFB $37 ;;greater-0 + DEFB $00 ;;jump-true + + DEFB $0D ;;to L2DF8, PF-POSTVE + +; must be zero itself + + DEFB $02 ;;delete + DEFB $38 ;;end-calc + + LD A,$30 ; prepare the character '0' + + RST 10H ; PRINT-A + RET ; return. -> +; --- + +;; PF-NEGTVE +L2DF2: DEFB $2A ;;abs + DEFB $38 ;;end-calc + + LD A,$2D ; the character '-' + + RST 10H ; PRINT-A + +; and continue to print the now positive number. + + RST 28H ;; FP-CALC + +;; PF-POSTVE +L2DF8: DEFB $A0 ;;stk-zero x,0. begin by + DEFB $C3 ;;st-mem-3 x,0. clearing a temporary + DEFB $C4 ;;st-mem-4 x,0. output buffer to + DEFB $C5 ;;st-mem-5 x,0. fifteen zeros. + DEFB $02 ;;delete x. + DEFB $38 ;;end-calc x. + + EXX ; in case called from 'str$' then save the + PUSH HL ; pointer to whatever comes after + EXX ; str$ as H'L' will be used. + +; now enter a loop? + +;; PF-LOOP +L2E01: RST 28H ;; FP-CALC + DEFB $31 ;;duplicate x,x. + DEFB $27 ;;int x,int x. + DEFB $C2 ;;st-mem-2 x,int x. + DEFB $03 ;;subtract x-int x. fractional part. + DEFB $E2 ;;get-mem-2 x-int x, int x. + DEFB $01 ;;exchange int x, x-int x. + DEFB $C2 ;;st-mem-2 int x, x-int x. + DEFB $02 ;;delete int x. + DEFB $38 ;;end-calc int x. + ; + ; mem-2 holds the fractional part. + +; HL points to last value int x + + LD A,(HL) ; fetch exponent of int x. + AND A ; test + JR NZ,L2E56 ; forward to PF-LARGE if a large integer + ; > 65535 + +; continue with small positive integer components in range 0 - 65535 +; if original number was say .999 then this integer component is zero. + + CALL L2D7F ; routine INT-FETCH gets x in DE + ; (but x is not deleted) + + LD B,$10 ; set B, bit counter, to 16d + + LD A,D ; test if + AND A ; high byte is zero + JR NZ,L2E1E ; forward to PF-SAVE if 16-bit integer. + +; and continue with integer in range 0 - 255. + + OR E ; test the low byte for zero + ; i.e. originally just point something or other. + JR Z,L2E24 ; forward if so to PF-SMALL + +; + + LD D,E ; transfer E to D + LD B,$08 ; and reduce the bit counter to 8. + +;; PF-SAVE +L2E1E: PUSH DE ; save the part before decimal point. + EXX ; + POP DE ; and pop in into D'E' + EXX ; + JR L2E7B ; forward to PF-BITS + +; --------------------- + +; the branch was here when 'int x' was found to be zero as in say 0.5. +; The zero has been fetched from the calculator stack but not deleted and +; this should occur now. This omission leaves the stack unbalanced and while +; that causes no problems with a simple PRINT statement, it will if str$ is +; being used in an expression e.g. "2" + STR$ 0.5 gives the result "0.5" +; instead of the expected result "20.5". +; credit Tony Stratton, 1982. +; A DEFB 02 delete is required immediately on using the calculator. + +;; PF-SMALL +L2E24: RST 28H ;; FP-CALC int x = 0. +L2E25: DEFB $E2 ;;get-mem-2 int x = 0, x-int x. + DEFB $38 ;;end-calc + + LD A,(HL) ; fetch exponent of positive fractional number + SUB $7E ; subtract + + CALL L2DC1 ; routine LOG(2^A) calculates leading digits. + + LD D,A ; transfer count to D + LD A,($5CAC) ; fetch total MEM-5-1 + SUB D ; + LD ($5CAC),A ; MEM-5-1 + LD A,D ; + CALL L2D4F ; routine E-TO-FP + + RST 28H ;; FP-CALC + DEFB $31 ;;duplicate + DEFB $27 ;;int + DEFB $C1 ;;st-mem-1 + DEFB $03 ;;subtract + DEFB $E1 ;;get-mem-1 + DEFB $38 ;;end-calc + + CALL L2DD5 ; routine FP-TO-A + + PUSH HL ; save HL + LD ($5CA1),A ; MEM-3-1 + DEC A ; + RLA ; + SBC A,A ; + INC A ; + + LD HL,$5CAB ; address MEM-5-1 leading digit counter + LD (HL),A ; store counter + INC HL ; address MEM-5-2 total digits + ADD A,(HL) ; add counter to contents + LD (HL),A ; and store updated value + POP HL ; restore HL + + JP L2ECF ; JUMP forward to PF-FRACTN + +; --- + +; Note. while it would be pedantic to comment on every occasion a JP +; instruction could be replaced with a JR instruction, this applies to the +; above, which is useful if you wish to correct the unbalanced stack error +; by inserting a 'DEFB 02 delete' at L2E25, and maintain main addresses. + +; the branch was here with a large positive integer > 65535 e.g. 123456789 +; the accumulator holds the exponent. + +;; PF-LARGE +L2E56: SUB $80 ; make exponent positive + CP $1C ; compare to 28 + JR C,L2E6F ; to PF-MEDIUM if integer <= 2^27 + + CALL L2DC1 ; routine LOG(2^A) + SUB $07 ; + LD B,A ; + LD HL,$5CAC ; address MEM-5-1 the leading digits counter. + ADD A,(HL) ; add A to contents + LD (HL),A ; store updated value. + LD A,B ; + NEG ; negate + CALL L2D4F ; routine E-TO-FP + JR L2E01 ; back to PF-LOOP + +; ---------------------------- + +;; PF-MEDIUM +L2E6F: EX DE,HL ; + CALL L2FBA ; routine FETCH-TWO + EXX ; + SET 7,D ; + LD A,L ; + EXX ; + SUB $80 ; + LD B,A ; + +; the branch was here to handle bits in DE with 8 or 16 in B if small int +; and integer in D'E', 6 nibbles will accommodate 065535 but routine does +; 32-bit numbers as well from above + +;; PF-BITS +L2E7B: SLA E ; C + +; --- + +;; PF-OUT-LP +L2F52: LD A,C ; fetch total digit count + AND A ; test for zero + JR Z,L2F59 ; forward to PF-OUT-DT if so + + LD A,(HL) ; fetch digit + INC HL ; address next digit + DEC C ; decrease total digit counter + +;; PF-OUT-DT +L2F59: CALL L15EF ; routine OUT-CODE outputs it. + DJNZ L2F52 ; loop back to PF-OUT-LP until B leading + ; digits output. + +;; PF-DC-OUT +L2F5E: LD A,C ; fetch total digits and + AND A ; test if also zero + RET Z ; return if so --> + +; + + INC B ; increment B + LD A,$2E ; prepare the character '.' + +;; PF-DEC-0$ +L2F64: RST 10H ; PRINT-A outputs the character '.' or '0' + + LD A,$30 ; prepare the character '0' + ; (for cases like .000012345678) + DJNZ L2F64 ; loop back to PF-DEC-0$ for B times. + + LD B,C ; load B with now trailing digit counter. + JR L2F52 ; back to PF-OUT-LP + +; --------------------------------- + +; the branch was here for E-format printing e.g 123456789 => 1.2345679e+8 + +;; PF-E-FRMT +L2F6C: LD D,B ; counter to D + DEC D ; decrement + LD B,$01 ; load B with 1. + + CALL L2F4A ; routine PF-E-SBRN above + + LD A,$45 ; prepare character 'e' + RST 10H ; PRINT-A + + LD C,D ; exponent to C + LD A,C ; and to A + AND A ; test exponent + JP P,L2F83 ; to PF-E-POS if positive + + NEG ; negate + LD C,A ; positive exponent to C + LD A,$2D ; prepare character '-' + JR L2F85 ; skip to PF-E-SIGN + +; --- + +;; PF-E-POS +L2F83: LD A,$2B ; prepare character '+' + +;; PF-E-SIGN +L2F85: RST 10H ; PRINT-A outputs the sign + + LD B,$00 ; make the high byte zero. + JP L1A1B ; exit via OUT-NUM-1 to print exponent in BC + +; ------------------------------ +; Handle printing floating point +; ------------------------------ +; This subroutine is called twice from above when printing floating-point +; numbers. It returns 10*A +C in registers C and A + +;; CA-10*A+C +L2F8B: PUSH DE ; preserve DE. + LD L,A ; transfer A to L + LD H,$00 ; zero high byte. + LD E,L ; copy HL + LD D,H ; to DE. + ADD HL,HL ; double (*2) + ADD HL,HL ; double (*4) + ADD HL,DE ; add DE (*5) + ADD HL,HL ; double (*10) + LD E,C ; copy C to E (D is 0) + ADD HL,DE ; and add to give required result. + LD C,H ; transfer to + LD A,L ; destination registers. + POP DE ; restore DE + RET ; return with result. + +; -------------- +; Prepare to add +; -------------- +; This routine is called twice by addition to prepare the two numbers. The +; exponent is picked up in A and the location made zero. Then the sign bit +; is tested before being set to the implied state. Negative numbers are twos +; complemented. + +;; PREP-ADD +L2F9B: LD A,(HL) ; pick up exponent + LD (HL),$00 ; make location zero + AND A ; test if number is zero + RET Z ; return if so + + INC HL ; address mantissa + BIT 7,(HL) ; test the sign bit + SET 7,(HL) ; set it to implied state + DEC HL ; point to exponent + RET Z ; return if positive number. + + PUSH BC ; preserve BC + LD BC,$0005 ; length of number + ADD HL,BC ; point HL past end + LD B,C ; set B to 5 counter + LD C,A ; store exponent in C + SCF ; set carry flag + +;; NEG-BYTE +L2FAF: DEC HL ; work from LSB to MSB + LD A,(HL) ; fetch byte + CPL ; complement + ADC A,$00 ; add in initial carry or from prev operation + LD (HL),A ; put back + DJNZ L2FAF ; loop to NEG-BYTE till all 5 done + + LD A,C ; stored exponent to A + POP BC ; restore original BC + RET ; return + +; ----------------- +; Fetch two numbers +; ----------------- +; This routine is called twice when printing floating point numbers and also +; to fetch two numbers by the addition, multiply and division routines. +; HL addresses the first number, DE addresses the second number. +; For arithmetic only, A holds the sign of the result which is stored in +; the second location. + +;; FETCH-TWO +L2FBA: PUSH HL ; save pointer to first number, result if math. + PUSH AF ; save result sign. + + LD C,(HL) ; + INC HL ; + + LD B,(HL) ; + LD (HL),A ; store the sign at correct location in + ; destination 5 bytes for arithmetic only. + INC HL ; + + LD A,C ; + LD C,(HL) ; + PUSH BC ; + INC HL ; + LD C,(HL) ; + INC HL ; + LD B,(HL) ; + EX DE,HL ; + LD D,A ; + LD E,(HL) ; + PUSH DE ; + INC HL ; + LD D,(HL) ; + INC HL ; + LD E,(HL) ; + PUSH DE ; + EXX ; + POP DE ; + POP HL ; + POP BC ; + EXX ; + INC HL ; + LD D,(HL) ; + INC HL ; + LD E,(HL) ; + + POP AF ; restore possible result sign. + POP HL ; and pointer to possible result. + RET ; return. + +; --------------------------------- +; Shift floating point number right +; --------------------------------- +; +; + +;; SHIFT-FP +L2FDD: AND A ; + RET Z ; + + CP $21 ; + JR NC,L2FF9 ; to ADDEND-0 + + PUSH BC ; + LD B,A ; + +;; ONE-SHIFT +L2FE5: EXX ; + SRA L ; + RR D ; + RR E ; + EXX ; + RR D ; + RR E ; + DJNZ L2FE5 ; to ONE-SHIFT + + POP BC ; + RET NC ; + + CALL L3004 ; routine ADD-BACK + RET NZ ; + +;; ADDEND-0 +L2FF9: EXX ; + XOR A ; + +;; ZEROS-4/5 +L2FFB: LD L,$00 ; + LD D,A ; + LD E,L ; + EXX ; + LD DE,$0000 ; + RET ; + +; ------------------ +; Add back any carry +; ------------------ +; +; + +;; ADD-BACK +L3004: INC E ; + RET NZ ; + + INC D ; + RET NZ ; + + EXX ; + INC E ; + JR NZ,L300D ; to ALL-ADDED + + INC D ; + +;; ALL-ADDED +L300D: EXX ; + RET ; + +; ----------------------- +; Handle subtraction (03) +; ----------------------- +; Subtraction is done by switching the sign byte/bit of the second number +; which may be integer of floating point and continuing into addition. + +;; subtract +L300F: EX DE,HL ; address second number with HL + + CALL L346E ; routine NEGATE switches sign + + EX DE,HL ; address first number again + ; and continue. + +; -------------------- +; Handle addition (0F) +; -------------------- +; HL points to first number, DE to second. +; If they are both integers, then go for the easy route. + +;; addition +L3014: LD A,(DE) ; fetch first byte of second + OR (HL) ; combine with first byte of first + JR NZ,L303E ; forward to FULL-ADDN if at least one was + ; in floating point form. + +; continue if both were small integers. + + PUSH DE ; save pointer to lowest number for result. + + INC HL ; address sign byte and + PUSH HL ; push the pointer. + + INC HL ; address low byte + LD E,(HL) ; to E + INC HL ; address high byte + LD D,(HL) ; to D + INC HL ; address unused byte + + INC HL ; address known zero indicator of 1st number + INC HL ; address sign byte + + LD A,(HL) ; sign to A, $00 or $FF + + INC HL ; address low byte + LD C,(HL) ; to C + INC HL ; address high byte + LD B,(HL) ; to B + + POP HL ; pop result sign pointer + EX DE,HL ; integer to HL + + ADD HL,BC ; add to the other one in BC + ; setting carry if overflow. + + EX DE,HL ; save result in DE bringing back sign pointer + + ADC A,(HL) ; if pos/pos A=01 with overflow else 00 + ; if neg/neg A=FF with overflow else FE + ; if mixture A=00 with overflow else FF + + RRCA ; bit 0 to (C) + + ADC A,$00 ; both acceptable signs now zero + + JR NZ,L303C ; forward to ADDN-OFLW if not + + SBC A,A ; restore a negative result sign + + LD (HL),A ; + INC HL ; + LD (HL),E ; + INC HL ; + LD (HL),D ; + DEC HL ; + DEC HL ; + DEC HL ; + + POP DE ; STKEND + RET ; + +; --- + +;; ADDN-OFLW +L303C: DEC HL ; + POP DE ; + +;; FULL-ADDN +L303E: CALL L3293 ; routine RE-ST-TWO + EXX ; + PUSH HL ; + EXX ; + PUSH DE ; + PUSH HL ; + CALL L2F9B ; routine PREP-ADD + LD B,A ; + EX DE,HL ; + CALL L2F9B ; routine PREP-ADD + LD C,A ; + CP B ; + JR NC,L3055 ; to SHIFT-LEN + + LD A,B ; + LD B,C ; + EX DE,HL ; + +;; SHIFT-LEN +L3055: PUSH AF ; + SUB B ; + CALL L2FBA ; routine FETCH-TWO + CALL L2FDD ; routine SHIFT-FP + POP AF ; + POP HL ; + LD (HL),A ; + PUSH HL ; + LD L,B ; + LD H,C ; + ADD HL,DE ; + EXX ; + EX DE,HL ; + ADC HL,BC ; + EX DE,HL ; + LD A,H ; + ADC A,L ; + LD L,A ; + RRA ; + XOR L ; + EXX ; + EX DE,HL ; + POP HL ; + RRA ; + JR NC,L307C ; to TEST-NEG + + LD A,$01 ; + CALL L2FDD ; routine SHIFT-FP + INC (HL) ; + JR Z,L309F ; to ADD-REP-6 + +;; TEST-NEG +L307C: EXX ; + LD A,L ; + AND $80 ; + EXX ; + INC HL ; + LD (HL),A ; + DEC HL ; + JR Z,L30A5 ; to GO-NC-MLT + + LD A,E ; + NEG ; Negate + CCF ; Complement Carry Flag + LD E,A ; + LD A,D ; + CPL ; + ADC A,$00 ; + LD D,A ; + EXX ; + LD A,E ; + CPL ; + ADC A,$00 ; + LD E,A ; + LD A,D ; + CPL ; + ADC A,$00 ; + JR NC,L30A3 ; to END-COMPL + + RRA ; + EXX ; + INC (HL) ; + +;; ADD-REP-6 +L309F: JP Z,L31AD ; to REPORT-6 + + EXX ; + +;; END-COMPL +L30A3: LD D,A ; + EXX ; + +;; GO-NC-MLT +L30A5: XOR A ; + JP L3155 ; to TEST-NORM + +; ----------------------------- +; Used in 16 bit multiplication +; ----------------------------- +; This routine is used, in the first instance, by the multiply calculator +; literal to perform an integer multiplication in preference to +; 32-bit multiplication to which it will resort if this overflows. +; +; It is also used by STK-VAR to calculate array subscripts and by DIM to +; calculate the space required for multi-dimensional arrays. + +;; HL-HL*DE +L30A9: PUSH BC ; preserve BC throughout + LD B,$10 ; set B to 16 + LD A,H ; save H in A high byte + LD C,L ; save L in C low byte + LD HL,$0000 ; initialize result to zero + +; now enter a loop. + +;; HL-LOOP +L30B1: ADD HL,HL ; double result + JR C,L30BE ; to HL-END if overflow + + RL C ; shift AC left into carry + RLA ; + JR NC,L30BC ; to HL-AGAIN to skip addition if no carry + + ADD HL,DE ; add in DE + JR C,L30BE ; to HL-END if overflow + +;; HL-AGAIN +L30BC: DJNZ L30B1 ; back to HL-LOOP for all 16 bits + +;; HL-END +L30BE: POP BC ; restore preserved BC + RET ; return with carry reset if successful + ; and result in HL. + +; ----------------------------- +; Prepare to multiply or divide +; ----------------------------- +; This routine is called in succession from multiply and divide to prepare +; two mantissas by setting the leftmost bit that is used for the sign. +; On the first call A holds zero and picks up the sign bit. On the second +; call the two bits are XORed to form the result sign - minus * minus giving +; plus etc. If either number is zero then this is flagged. +; HL addresses the exponent. + +;; PREP-M/D +L30C0: CALL L34E9 ; routine TEST-ZERO preserves accumulator. + RET C ; return carry set if zero + + INC HL ; address first byte of mantissa + XOR (HL) ; pick up the first or xor with first. + SET 7,(HL) ; now set to give true 32-bit mantissa + DEC HL ; point to exponent + RET ; return with carry reset + +; -------------------------- +; Handle multiplication (04) +; -------------------------- +; +; + +;; multiply +L30CA: LD A,(DE) ; + OR (HL) ; + JR NZ,L30F0 ; to MULT-LONG + + PUSH DE ; + PUSH HL ; + PUSH DE ; + CALL L2D7F ; routine INT-FETCH + EX DE,HL ; + EX (SP),HL ; + LD B,C ; + CALL L2D7F ; routine INT-FETCH + LD A,B ; + XOR C ; + LD C,A ; + POP HL ; + CALL L30A9 ; routine HL-HL*DE + EX DE,HL ; + POP HL ; + JR C,L30EF ; to MULT-OFLW + + LD A,D ; + OR E ; + JR NZ,L30EA ; to MULT-RSLT + + LD C,A ; + +;; MULT-RSLT +L30EA: CALL L2D8E ; routine INT-STORE + POP DE ; + RET ; + +; --- + +;; MULT-OFLW +L30EF: POP DE ; + +;; MULT-LONG +L30F0: CALL L3293 ; routine RE-ST-TWO + XOR A ; + CALL L30C0 ; routine PREP-M/D + RET C ; + + EXX ; + PUSH HL ; + EXX ; + PUSH DE ; + EX DE,HL ; + CALL L30C0 ; routine PREP-M/D + EX DE,HL ; + JR C,L315D ; to ZERO-RSLT + + PUSH HL ; + CALL L2FBA ; routine FETCH-TWO + LD A,B ; + AND A ; + SBC HL,HL ; + EXX ; + PUSH HL ; + SBC HL,HL ; + EXX ; + LD B,$21 ; + JR L3125 ; to STRT-MLT + +; --- + +;; MLT-LOOP +L3114: JR NC,L311B ; to NO-ADD + + ADD HL,DE ; + EXX ; + ADC HL,DE ; + EXX ; + +;; NO-ADD +L311B: EXX ; + RR H ; + RR L ; + EXX ; + RR H ; + RR L ; + +;; STRT-MLT +L3125: EXX ; + RR B ; + RR C ; + EXX ; + RR C ; + RRA ; + DJNZ L3114 ; to MLT-LOOP + + EX DE,HL ; + EXX ; + EX DE,HL ; + EXX ; + POP BC ; + POP HL ; + LD A,B ; + ADD A,C ; + JR NZ,L313B ; to MAKE-EXPT + + AND A ; + +;; MAKE-EXPT +L313B: DEC A ; + CCF ; Complement Carry Flag + +;; DIVN-EXPT +L313D: RLA ; + CCF ; Complement Carry Flag + RRA ; + JP P,L3146 ; to OFLW1-CLR + + JR NC,L31AD ; to REPORT-6 + + AND A ; + +;; OFLW1-CLR +L3146: INC A ; + JR NZ,L3151 ; to OFLW2-CLR + + JR C,L3151 ; to OFLW2-CLR + + EXX ; + BIT 7,D ; + EXX ; + JR NZ,L31AD ; to REPORT-6 + +;; OFLW2-CLR +L3151: LD (HL),A ; + EXX ; + LD A,B ; + EXX ; + +;; TEST-NORM +L3155: JR NC,L316C ; to NORMALISE + + LD A,(HL) ; + AND A ; + +;; NEAR-ZERO +L3159: LD A,$80 ; + JR Z,L315E ; to SKIP-ZERO + +;; ZERO-RSLT +L315D: XOR A ; + +;; SKIP-ZERO +L315E: EXX ; + AND D ; + CALL L2FFB ; routine ZEROS-4/5 + RLCA ; + LD (HL),A ; + JR C,L3195 ; to OFLOW-CLR + + INC HL ; + LD (HL),A ; + DEC HL ; + JR L3195 ; to OFLOW-CLR + +; --- + +;; NORMALISE +L316C: LD B,$20 ; + +;; SHIFT-ONE +L316E: EXX ; + BIT 7,D ; + EXX ; + JR NZ,L3186 ; to NORML-NOW + + RLCA ; + RL E ; + RL D ; + EXX ; + RL E ; + RL D ; + EXX ; + DEC (HL) ; + JR Z,L3159 ; to NEAR-ZERO + + DJNZ L316E ; to SHIFT-ONE + + JR L315D ; to ZERO-RSLT + +; --- + +;; NORML-NOW +L3186: RLA ; + JR NC,L3195 ; to OFLOW-CLR + + CALL L3004 ; routine ADD-BACK + JR NZ,L3195 ; to OFLOW-CLR + + EXX ; + LD D,$80 ; + EXX ; + INC (HL) ; + JR Z,L31AD ; to REPORT-6 + +;; OFLOW-CLR +L3195: PUSH HL ; + INC HL ; + EXX ; + PUSH DE ; + EXX ; + POP BC ; + LD A,B ; + RLA ; + RL (HL) ; + RRA ; + LD (HL),A ; + INC HL ; + LD (HL),C ; + INC HL ; + LD (HL),D ; + INC HL ; + LD (HL),E ; + POP HL ; + POP DE ; + EXX ; + POP HL ; + EXX ; + RET ; + +; --- + +;; REPORT-6 +L31AD: RST 08H ; ERROR-1 + DEFB $05 ; Error Report: Number too big + +; -------------------- +; Handle division (05) +; -------------------- +; +; + +;; division +L31AF: CALL L3293 ; routine RE-ST-TWO + EX DE,HL ; + XOR A ; + CALL L30C0 ; routine PREP-M/D + JR C,L31AD ; to REPORT-6 + + EX DE,HL ; + CALL L30C0 ; routine PREP-M/D + RET C ; + + EXX ; + PUSH HL ; + EXX ; + PUSH DE ; + PUSH HL ; + CALL L2FBA ; routine FETCH-TWO + EXX ; + PUSH HL ; + LD H,B ; + LD L,C ; + EXX ; + LD H,C ; + LD L,B ; + XOR A ; + LD B,$DF ; + JR L31E2 ; to DIV-START + +; --- + +;; DIV-LOOP +L31D2: RLA ; + RL C ; + EXX ; + RL C ; + RL B ; + EXX ; + +;; div-34th +L31DB: ADD HL,HL ; + EXX ; + ADC HL,HL ; + EXX ; + JR C,L31F2 ; to SUBN-ONLY + +;; DIV-START +L31E2: SBC HL,DE ; + EXX ; + SBC HL,DE ; + EXX ; + JR NC,L31F9 ; to NO-RSTORE + + ADD HL,DE ; + EXX ; + ADC HL,DE ; + EXX ; + AND A ; + JR L31FA ; to COUNT-ONE + +; --- + +;; SUBN-ONLY +L31F2: AND A ; + SBC HL,DE ; + EXX ; + SBC HL,DE ; + EXX ; + +;; NO-RSTORE +L31F9: SCF ; Set Carry Flag + +;; COUNT-ONE +L31FA: INC B ; + JP M,L31D2 ; to DIV-LOOP + + PUSH AF ; + JR Z,L31E2 ; to DIV-START + +; +; +; +; + + LD E,A ; + LD D,C ; + EXX ; + LD E,C ; + LD D,B ; + POP AF ; + RR B ; + POP AF ; + RR B ; + EXX ; + POP BC ; + POP HL ; + LD A,B ; + SUB C ; + JP L313D ; jump back to DIVN-EXPT + +; ------------------------------------ +; Integer truncation towards zero ($3A) +; ------------------------------------ +; +; + +;; truncate +L3214: LD A,(HL) ; + AND A ; + RET Z ; + + CP $81 ; + JR NC,L3221 ; to T-GR-ZERO + + LD (HL),$00 ; + LD A,$20 ; + JR L3272 ; to NIL-BYTES + +; --- + +;; T-GR-ZERO +L3221: CP $91 ; + JR NZ,L323F ; to T-SMALL + + INC HL ; + INC HL ; + INC HL ; + LD A,$80 ; + AND (HL) ; + DEC HL ; + OR (HL) ; + DEC HL ; + JR NZ,L3233 ; to T-FIRST + + LD A,$80 ; + XOR (HL) ; + +;; T-FIRST +L3233: DEC HL ; + JR NZ,L326C ; to T-EXPNENT + + LD (HL),A ; + INC HL ; + LD (HL),$FF ; + DEC HL ; + LD A,$18 ; + JR L3272 ; to NIL-BYTES + +; --- + +;; T-SMALL +L323F: JR NC,L326D ; to X-LARGE + + PUSH DE ; + CPL ; + ADD A,$91 ; + INC HL ; + LD D,(HL) ; + INC HL ; + LD E,(HL) ; + DEC HL ; + DEC HL ; + LD C,$00 ; + BIT 7,D ; + JR Z,L3252 ; to T-NUMERIC + + DEC C ; + +;; T-NUMERIC +L3252: SET 7,D ; + LD B,$08 ; + SUB B ; + ADD A,B ; + JR C,L325E ; to T-TEST + + LD E,D ; + LD D,$00 ; + SUB B ; + +;; T-TEST +L325E: JR Z,L3267 ; to T-STORE + + LD B,A ; + +;; T-SHIFT +L3261: SRL D ; + RR E ; + DJNZ L3261 ; to T-SHIFT + +;; T-STORE +L3267: CALL L2D8E ; routine INT-STORE + POP DE ; + RET ; + +; --- + +;; T-EXPNENT +L326C: LD A,(HL) ; + +;; X-LARGE +L326D: SUB $A0 ; + RET P ; + + NEG ; Negate + +;; NIL-BYTES +L3272: PUSH DE ; + EX DE,HL ; + DEC HL ; + LD B,A ; + SRL B ; + SRL B ; + SRL B ; + JR Z,L3283 ; to BITS-ZERO + +;; BYTE-ZERO +L327E: LD (HL),$00 ; + DEC HL ; + DJNZ L327E ; to BYTE-ZERO + +;; BITS-ZERO +L3283: AND $07 ; + JR Z,L3290 ; to IX-END + + LD B,A ; + LD A,$FF ; + +;; LESS-MASK +L328A: SLA A ; + DJNZ L328A ; to LESS-MASK + + AND (HL) ; + LD (HL),A ; + +;; IX-END +L3290: EX DE,HL ; + POP DE ; + RET ; + +; ---------------------------------- +; Storage of numbers in 5 byte form. +; ================================== +; Both integers and floating-point numbers can be stored in five bytes. +; Zero is a special case stored as 5 zeros. +; For integers the form is +; Byte 1 - zero, +; Byte 2 - sign byte, $00 +ve, $FF -ve. +; Byte 3 - Low byte of integer. +; Byte 4 - High byte +; Byte 5 - unused but always zero. +; +; it seems unusual to store the low byte first but it is just as easy either +; way. Statistically it just increases the chances of trailing zeros which +; is an advantage elsewhere in saving ROM code. +; +; zero sign low high unused +; So +1 is 00000000 00000000 00000001 00000000 00000000 +; +; and -1 is 00000000 11111111 11111111 11111111 00000000 +; +; much of the arithmetic found in BASIC lines can be done using numbers +; in this form using the Z80's 16 bit register operation ADD. +; (multiplication is done by a sequence of additions). +; +; Storing -ve integers in two's complement form, means that they are ready for +; addition and you might like to add the numbers above to prove that the +; answer is zero. If, as in this case, the carry is set then that denotes that +; the result is positive. This only applies when the signs don't match. +; With positive numbers a carry denotes the result is out of integer range. +; With negative numbers a carry denotes the result is within range. +; The exception to the last rule is when the result is -65536 +; +; Floating point form is an alternative method of storing numbers which can +; be used for integers and larger (or fractional) numbers. +; +; In this form 1 is stored as +; 10000001 00000000 00000000 00000000 00000000 +; +; When a small integer is converted to a floating point number the last two +; bytes are always blank so they are omitted in the following steps +; +; first make exponent +1 +16d (bit 7 of the exponent is set if positive) + +; 10010001 00000000 00000001 +; 10010000 00000000 00000010 <- now shift left and decrement exponent +; ... +; 10000010 01000000 00000000 <- until a 1 abuts the imaginary point +; 10000001 10000000 00000000 to the left of the mantissa. +; +; however since the leftmost bit of the mantissa is always set then it can +; be used to denote the sign of the mantissa and put back when needed by the +; PREP routines which gives +; +; 10000001 00000000 00000000 + +; ----------------------------- +; Re-stack two `small' integers +; ----------------------------- +; This routine is called to re-stack two numbers in full floating point form +; e.g. from mult when integer multiplication has overflowed. + +;; RE-ST-TWO +L3293: CALL L3296 ; routine RESTK-SUB below and continue + ; into the routine to do the other one. + +;; RESTK-SUB +L3296: EX DE,HL ; swap pointers + +; -------------------------------- +; Re-stack one number in full form +; -------------------------------- +; This routine re-stacks an integer usually on the calculator stack +; in full floating point form. +; HL points to first byte. + +;; re-stack +L3297: LD A,(HL) ; Fetch Exponent byte to A + AND A ; test it + RET NZ ; return if not zero as already in full + ; floating-point form. + + PUSH DE ; preserve DE. + CALL L2D7F ; routine INT-FETCH + ; integer to DE, sign to C. + +; HL points to 4th byte. + + XOR A ; clear accumulator. + INC HL ; point to 5th. + LD (HL),A ; and blank. + DEC HL ; point to 4th. + LD (HL),A ; and blank. + + LD B,$91 ; set exponent byte +ve $81 + ; and imaginary dec point 16 bits to right + ; of first bit. + +; we could skip to normalize now but it's quicker to avoid +; normalizing through an empty D. + + LD A,D ; fetch the high byte D + AND A ; is it zero ? + JR NZ,L32B1 ; skip to RS-NRMLSE if not. + + OR E ; low byte E to A and test for zero + LD B,D ; set B exponent to 0 + JR Z,L32BD ; forward to RS-STORE if value is zero. + + LD D,E ; transfer E to D + LD E,B ; set E to 0 + LD B,$89 ; reduce the initial exponent by eight. + + +;; RS-NRMLSE +L32B1: EX DE,HL ; integer to HL, addr of 4th byte to DE. + +;; RSTK-LOOP +L32B2: DEC B ; decrease exponent + ADD HL,HL ; shift DE left + JR NC,L32B2 ; loop back to RSTK-LOOP + ; until a set bit pops into carry + + RRC C ; now rotate the sign byte $00 or $FF + ; into carry to give a sign bit + + RR H ; rotate the sign bit to left of H + RR L ; rotate any carry into L + + EX DE,HL ; address 4th byte, normalized int to DE + +;; RS-STORE +L32BD: DEC HL ; address 3rd byte + LD (HL),E ; place E + DEC HL ; address 2nd byte + LD (HL),D ; place D + DEC HL ; address 1st byte + LD (HL),B ; store the exponent + + POP DE ; restore initial DE. + RET ; return. + +;**************************************** +;** Part 10. FLOATING-POINT CALCULATOR ** +;**************************************** + +; As a general rule the calculator avoids using the IY register. +; exceptions are val, val$ and str$. +; So an assembly language programmer who has disabled interrupts to use +; IY for other purposes can still use the calculator for mathematical +; purposes. + + +; ------------------ +; Table of constants +; ------------------ +; +; + +; used 11 times +;; stk-zero 00 00 00 00 00 +L32C5: DEFB $00 ;;Bytes: 1 + DEFB $B0 ;;Exponent $00 + DEFB $00 ;;(+00,+00,+00) + +; used 19 times +;; stk-one 00 00 01 00 00 +L32C8: DEFB $40 ;;Bytes: 2 + DEFB $B0 ;;Exponent $00 + DEFB $00,$01 ;;(+00,+00) + +; used 9 times +;; stk-half 80 00 00 00 00 +L32CC: DEFB $30 ;;Exponent: $80, Bytes: 1 + DEFB $00 ;;(+00,+00,+00) + +; used 4 times. +;; stk-pi/2 81 49 0F DA A2 +L32CE: DEFB $F1 ;;Exponent: $81, Bytes: 4 + DEFB $49,$0F,$DA,$A2 ;; + +; used 3 times. +;; stk-ten 00 00 0A 00 00 +L32D3: DEFB $40 ;;Bytes: 2 + DEFB $B0 ;;Exponent $00 + DEFB $00,$0A ;;(+00,+00) + + +; ------------------ +; Table of addresses +; ------------------ +; +; starts with binary operations which have two operands and one result. +; three pseudo binary operations first. + +;; tbl-addrs +L32D7: DEFW L368F ; $00 Address: $368F - jump-true + DEFW L343C ; $01 Address: $343C - exchange + DEFW L33A1 ; $02 Address: $33A1 - delete + +; true binary operations. + + DEFW L300F ; $03 Address: $300F - subtract + DEFW L30CA ; $04 Address: $30CA - multiply + DEFW L31AF ; $05 Address: $31AF - division + DEFW L3851 ; $06 Address: $3851 - to-power + DEFW L351B ; $07 Address: $351B - or + + DEFW L3524 ; $08 Address: $3524 - no-&-no + DEFW L353B ; $09 Address: $353B - no-l-eql + DEFW L353B ; $0A Address: $353B - no-gr-eql + DEFW L353B ; $0B Address: $353B - nos-neql + DEFW L353B ; $0C Address: $353B - no-grtr + DEFW L353B ; $0D Address: $353B - no-less + DEFW L353B ; $0E Address: $353B - nos-eql + DEFW L3014 ; $0F Address: $3014 - addition + + DEFW L352D ; $10 Address: $352D - str-&-no + DEFW L353B ; $11 Address: $353B - str-l-eql + DEFW L353B ; $12 Address: $353B - str-gr-eql + DEFW L353B ; $13 Address: $353B - strs-neql + DEFW L353B ; $14 Address: $353B - str-grtr + DEFW L353B ; $15 Address: $353B - str-less + DEFW L353B ; $16 Address: $353B - strs-eql + DEFW L359C ; $17 Address: $359C - strs-add + +; unary follow + + DEFW L35DE ; $18 Address: $35DE - val$ + DEFW L34BC ; $19 Address: $34BC - usr-$ + DEFW L3645 ; $1A Address: $3645 - read-in + DEFW L346E ; $1B Address: $346E - negate + + DEFW L3669 ; $1C Address: $3669 - code + DEFW L35DE ; $1D Address: $35DE - val + DEFW L3674 ; $1E Address: $3674 - len + DEFW L37B5 ; $1F Address: $37B5 - sin + DEFW L37AA ; $20 Address: $37AA - cos + DEFW L37DA ; $21 Address: $37DA - tan + DEFW L3833 ; $22 Address: $3833 - asn + DEFW L3843 ; $23 Address: $3843 - acs + DEFW L37E2 ; $24 Address: $37E2 - atn + DEFW L3713 ; $25 Address: $3713 - ln + DEFW L36C4 ; $26 Address: $36C4 - exp + DEFW L36AF ; $27 Address: $36AF - int + DEFW L384A ; $28 Address: $384A - sqr + DEFW L3492 ; $29 Address: $3492 - sgn + DEFW L346A ; $2A Address: $346A - abs + DEFW L34AC ; $2B Address: $34AC - peek + DEFW L34A5 ; $2C Address: $34A5 - in + DEFW L34B3 ; $2D Address: $34B3 - usr-no + DEFW L361F ; $2E Address: $361F - str$ + DEFW L35C9 ; $2F Address: $35C9 - chrs + DEFW L3501 ; $30 Address: $3501 - not + +; end of true unary + + DEFW L33C0 ; $31 Address: $33C0 - duplicate + DEFW L36A0 ; $32 Address: $36A0 - n-mod-m + DEFW L3686 ; $33 Address: $3686 - jump + DEFW L33C6 ; $34 Address: $33C6 - stk-data + DEFW L367A ; $35 Address: $367A - dec-jr-nz + DEFW L3506 ; $36 Address: $3506 - less-0 + DEFW L34F9 ; $37 Address: $34F9 - greater-0 + DEFW L369B ; $38 Address: $369B - end-calc + DEFW L3783 ; $39 Address: $3783 - get-argt + DEFW L3214 ; $3A Address: $3214 - truncate + DEFW L33A2 ; $3B Address: $33A2 - fp-calc-2 + DEFW L2D4F ; $3C Address: $2D4F - e-to-fp + DEFW L3297 ; $3D Address: $3297 - re-stack + +; the following are just the next available slots for the 128 compound literals +; which are in range $80 - $FF. + + DEFW L3449 ; $3E Address: $3449 - series-xx $80 - $9F. + DEFW L341B ; $3F Address: $341B - stk-const-xx $A0 - $BF. + DEFW L342D ; $40 Address: $342D - st-mem-xx $C0 - $DF. + DEFW L340F ; $41 Address: $340F - get-mem-xx $E0 - $FF. + +; Aside: 3E - 7F are therefore unused calculator literals. +; 3E - 7B would be available for expansion. + +; -------------- +; The Calculator +; -------------- +; +; + +;; CALCULATE +L335B: CALL L35BF ; routine STK-PNTRS is called to set up the + ; calculator stack pointers for a default + ; unary operation. HL = last value on stack. + ; DE = STKEND first location after stack. + +; the calculate routine is called at this point by the series generator... + +;; GEN-ENT-1 +L335E: LD A,B ; fetch the Z80 B register to A + LD ($5C67),A ; and store value in system variable BREG. + ; this will be the counter for dec-jr-nz + ; or if used from fp-calc2 the calculator + ; instruction. + +; ... and again later at this point + +;; GEN-ENT-2 +L3362: EXX ; switch sets + EX (SP),HL ; and store the address of next instruction, + ; the return address, in H'L'. + ; If this is a recursive call the the H'L' + ; of the previous invocation goes on stack. + ; c.f. end-calc. + EXX ; switch back to main set + +; this is the re-entry looping point when handling a string of literals. + +;; RE-ENTRY +L3365: LD ($5C65),DE ; save end of stack in system variable STKEND + EXX ; switch to alt + LD A,(HL) ; get next literal + INC HL ; increase pointer' + +; single operation jumps back to here + +;; SCAN-ENT +L336C: PUSH HL ; save pointer on stack + AND A ; now test the literal + JP P,L3380 ; forward to FIRST-3D if in range $00 - $3D + ; anything with bit 7 set will be one of + ; 128 compound literals. + +; compound literals have the following format. +; bit 7 set indicates compound. +; bits 6-5 the subgroup 0-3. +; bits 4-0 the embedded parameter $00 - $1F. +; The subgroup 0-3 needs to be manipulated to form the next available four +; address places after the simple literals in the address table. + + LD D,A ; save literal in D + AND $60 ; and with 01100000 to isolate subgroup + RRCA ; rotate bits + RRCA ; 4 places to right + RRCA ; not five as we need offset * 2 + RRCA ; 00000xx0 + ADD A,$7C ; add ($3E * 2) to give correct offset. + ; alter above if you add more literals. + LD L,A ; store in L for later indexing. + LD A,D ; bring back compound literal + AND $1F ; use mask to isolate parameter bits + JR L338E ; forward to ENT-TABLE + +; --- + +; the branch was here with simple literals. + +;; FIRST-3D +L3380: CP $18 ; compare with first unary operations. + JR NC,L338C ; to DOUBLE-A with unary operations + +; it is binary so adjust pointers. + + EXX ; + LD BC,$FFFB ; the value -5 + LD D,H ; transfer HL, the last value, to DE. + LD E,L ; + ADD HL,BC ; subtract 5 making HL point to second + ; value. + EXX ; + +;; DOUBLE-A +L338C: RLCA ; double the literal + LD L,A ; and store in L for indexing + +;; ENT-TABLE +L338E: LD DE,L32D7 ; Address: tbl-addrs + LD H,$00 ; prepare to index + ADD HL,DE ; add to get address of routine + LD E,(HL) ; low byte to E + INC HL ; + LD D,(HL) ; high byte to D + LD HL,L3365 ; Address: RE-ENTRY + EX (SP),HL ; goes to stack + PUSH DE ; now address of routine + EXX ; main set + ; avoid using IY register. + LD BC,($5C66) ; STKEND_hi + ; nothing much goes to C but BREG to B + ; and continue into next ret instruction + ; which has a dual identity + + +; ------------------ +; Handle delete (02) +; ------------------ +; A simple return but when used as a calculator literal this +; deletes the last value from the calculator stack. +; On entry, as always with binary operations, +; HL=first number, DE=second number +; On exit, HL=result, DE=stkend. +; So nothing to do + +;; delete +L33A1: RET ; return - indirect jump if from above. + +; --------------------- +; Single operation (3B) +; --------------------- +; this single operation is used, in the first instance, to evaluate most +; of the mathematical and string functions found in BASIC expressions. + +;; fp-calc-2 +L33A2: POP AF ; drop return address. + LD A,($5C67) ; load accumulator from system variable BREG + ; value will be literal eg. 'tan' + EXX ; switch to alt + JR L336C ; back to SCAN-ENT + ; next literal will be end-calc at L2758 + +; ---------------- +; Test five-spaces +; ---------------- +; This routine is called from MOVE-FP, STK-CONST and STK-STORE to +; test that there is enough space between the calculator stack and the +; machine stack for another five-byte value. It returns with BC holding +; the value 5 ready for any subsequent LDIR. + +;; TEST-5-SP +L33A9: PUSH DE ; save + PUSH HL ; registers + LD BC,$0005 ; an overhead of five bytes + CALL L1F05 ; routine TEST-ROOM tests free RAM raising + ; an error if not. + POP HL ; else restore + POP DE ; registers. + RET ; return with BC set at 5. + +; ------------ +; Stack number +; ------------ +; This routine is called to stack a hidden floating point number found in +; a BASIC line. It is also called to stack a numeric variable value, and +; from BEEP, to stack an entry in the semi-tone table. It is not part of the +; calculator suite of routines. +; On entry HL points to the number to be stacked. + +;; STACK-NUM +L33B4: LD DE,($5C65) ; load destination from STKEND system variable. + CALL L33C0 ; routine MOVE-FP puts on calculator stack + ; with a memory check. + LD ($5C65),DE ; set STKEND to next free location. + RET ; return. + +; --------------------------------- +; Move a floating point number (31) +; --------------------------------- +; This simple routine is a 5-byte LDIR instruction +; that incorporates a memory check. +; When used as a calculator literal it duplicates the last value on the +; calculator stack. +; Unary so on entry HL points to last value, DE to stkend + +;; duplicate +;; MOVE-FP +L33C0: CALL L33A9 ; routine TEST-5-SP test free memory + ; and sets BC to 5. + LDIR ; copy the five bytes. + RET ; return with DE addressing new STKEND + ; and HL addressing new last value. + +; ------------------- +; Stack literals ($34) +; ------------------- +; When a calculator subroutine needs to put a value on the calculator +; stack that is not a regular constant this routine is called with a +; variable number of following data bytes that convey to the routine +; the integer or floating point form as succinctly as is possible. + +;; stk-data +L33C6: LD H,D ; transfer STKEND + LD L,E ; to HL for result. + +;; STK-CONST +L33C8: CALL L33A9 ; routine TEST-5-SP tests that room exists + ; and sets BC to $05. + + EXX ; switch to alternate set + PUSH HL ; save the pointer to next literal on stack + EXX ; switch back to main set + + EX (SP),HL ; pointer to HL, destination to stack. + + PUSH BC ; save BC - value 5 from test room ??. + + LD A,(HL) ; fetch the byte following 'stk-data' + AND $C0 ; isolate bits 7 and 6 + RLCA ; rotate + RLCA ; to bits 1 and 0 range $00 - $03. + LD C,A ; transfer to C + INC C ; and increment to give number of bytes + ; to read. $01 - $04 + LD A,(HL) ; reload the first byte + AND $3F ; mask off to give possible exponent. + JR NZ,L33DE ; forward to FORM-EXP if it was possible to + ; include the exponent. + +; else byte is just a byte count and exponent comes next. + + INC HL ; address next byte and + LD A,(HL) ; pick up the exponent ( - $50). + +;; FORM-EXP +L33DE: ADD A,$50 ; now add $50 to form actual exponent + LD (DE),A ; and load into first destination byte. + LD A,$05 ; load accumulator with $05 and + SUB C ; subtract C to give count of trailing + ; zeros plus one. + INC HL ; increment source + INC DE ; increment destination + LD B,$00 ; prepare to copy + LDIR ; copy C bytes + + POP BC ; restore 5 counter to BC ??. + + EX (SP),HL ; put HL on stack as next literal pointer + ; and the stack value - result pointer - + ; to HL. + + EXX ; switch to alternate set. + POP HL ; restore next literal pointer from stack + ; to H'L'. + EXX ; switch back to main set. + + LD B,A ; zero count to B + XOR A ; clear accumulator + +;; STK-ZEROS +L33F1: DEC B ; decrement B counter + RET Z ; return if zero. >> + ; DE points to new STKEND + ; HL to new number. + + LD (DE),A ; else load zero to destination + INC DE ; increase destination + JR L33F1 ; loop back to STK-ZEROS until done. + +; ------------------------------- +; THE 'SKIP CONSTANTS' SUBROUTINE +; ------------------------------- +; This routine traverses variable-length entries in the table of constants, +; stacking intermediate, unwanted constants onto a dummy calculator stack, +; in the first five bytes of ROM. The destination DE normally points to the +; end of the calculator stack which might be in the normal place or in the +; system variables area during E-LINE-NO; INT-TO-FP; stk-ten. In any case, +; it would be simpler all round if the routine just shoved unwanted values +; where it is going to stick the wanted value. +; The instruction LD DE, $0000 can be removed. + +;; SKIP-CONS +L33F7: AND A ; test if initially zero. + +;; SKIP-NEXT +L33F8: RET Z ; return if zero. >> + + PUSH AF ; save count. + PUSH DE ; and normal STKEND + + LD DE,$0000 ; dummy value for STKEND at start of ROM + ; Note. not a fault but this has to be + ; moved elsewhere when running in RAM. + ; e.g. with Expandor Systems 'Soft ROM'. + ; Better still, write to the normal place. + CALL L33C8 ; routine STK-CONST works through variable + ; length records. + + POP DE ; restore real STKEND + POP AF ; restore count + DEC A ; decrease + JR L33F8 ; loop back to SKIP-NEXT + +; --------------- +; Memory location +; --------------- +; This routine, when supplied with a base address in HL and an index in A +; will calculate the address of the A'th entry, where each entry occupies +; five bytes. It is used for reading the semi-tone table and addressing +; floating-point numbers in the calculator's memory area. + +;; LOC-MEM +L3406: LD C,A ; store the original number $00-$1F. + RLCA ; double. + RLCA ; quadruple. + ADD A,C ; now add original to multiply by five. + + LD C,A ; place the result in C. + LD B,$00 ; set B to 0. + ADD HL,BC ; add to form address of start of number in HL. + RET ; return. + +; ------------------------------ +; Get from memory area ($E0 etc.) +; ------------------------------ +; Literals $E0 to $FF +; A holds $00-$1F offset. +; The calculator stack increases by 5 bytes. + +;; get-mem-xx +L340F: PUSH DE ; save STKEND + LD HL,($5C68) ; MEM is base address of the memory cells. + CALL L3406 ; routine LOC-MEM so that HL = first byte + CALL L33C0 ; routine MOVE-FP moves 5 bytes with memory + ; check. + ; DE now points to new STKEND. + POP HL ; original STKEND is now RESULT pointer. + RET ; return. + +; -------------------------- +; Stack a constant (A0 etc.) +; -------------------------- +; This routine allows a one-byte instruction to stack up to 32 constants +; held in short form in a table of constants. In fact only 5 constants are +; required. On entry the A register holds the literal ANDed with 1F. +; It isn't very efficient and it would have been better to hold the +; numbers in full, five byte form and stack them in a similar manner +; to that used for semi-tone table values. + +;; stk-const-xx +L341B: LD H,D ; save STKEND - required for result + LD L,E ; + EXX ; swap + PUSH HL ; save pointer to next literal + LD HL,L32C5 ; Address: stk-zero - start of table of + ; constants + EXX ; + CALL L33F7 ; routine SKIP-CONS + CALL L33C8 ; routine STK-CONST + EXX ; + POP HL ; restore pointer to next literal. + EXX ; + RET ; return. + +; -------------------------------- +; Store in a memory area ($C0 etc.) +; -------------------------------- +; Offsets $C0 to $DF +; Although 32 memory storage locations can be addressed, only six +; $C0 to $C5 are required by the ROM and only the thirty bytes (6*5) +; required for these are allocated. Spectrum programmers who wish to +; use the floating point routines from assembly language may wish to +; alter the system variable MEM to point to 160 bytes of RAM to have +; use the full range available. +; A holds the derived offset $00-$1F. +; This is a unary operation, so on entry HL points to the last value and DE +; points to STKEND. + +;; st-mem-xx +L342D: PUSH HL ; save the result pointer. + EX DE,HL ; transfer to DE. + LD HL,($5C68) ; fetch MEM the base of memory area. + CALL L3406 ; routine LOC-MEM sets HL to the destination. + EX DE,HL ; swap - HL is start, DE is destination. + CALL L33C0 ; routine MOVE-FP. + ; note. a short ld bc,5; ldir + ; the embedded memory check is not required + ; so these instructions would be faster. + EX DE,HL ; DE = STKEND + POP HL ; restore original result pointer + RET ; return. + +; ------------------------------------ +; Swap first number with second number +; ------------------------------------ +; This routine exchanges the last two values on the calculator stack +; On entry, as always with binary operations, +; HL=first number, DE=second number +; On exit, HL=result, DE=stkend. + +;; exchange +L343C: LD B,$05 ; there are five bytes to be swapped + +; start of loop. + +;; SWAP-BYTE +L343E: LD A,(DE) ; each byte of second + LD C,(HL) ; each byte of first + EX DE,HL ; swap pointers + LD (DE),A ; store each byte of first + LD (HL),C ; store each byte of second + INC HL ; advance both + INC DE ; pointers. + DJNZ L343E ; loop back to SWAP-BYTE until all 5 done. + + EX DE,HL ; even up the exchanges + ; so that DE addresses STKEND. + RET ; return. + +; -------------------------- +; Series generator (86 etc.) +; -------------------------- +; The Spectrum uses Chebyshev polynomials to generate approximations for +; SIN, ATN, LN and EXP. These are named after the Russian mathematician +; Pafnuty Chebyshev, born in 1821, who did much pioneering work on numerical +; series. As far as calculators are concerned, Chebyshev polynomials have an +; advantage over other series, for example the Taylor series, as they can +; reach an approximation in just six iterations for SIN, eight for EXP and +; twelve for LN and ATN. The mechanics of the routine are interesting but +; for full treatment of how these are generated with demonstrations in +; Sinclair BASIC see "The Complete Spectrum ROM Disassembly" by Dr Ian Logan +; and Dr Frank O'Hara, published 1983 by Melbourne House. + +;; series-xx +L3449: LD B,A ; parameter $00 - $1F to B counter + CALL L335E ; routine GEN-ENT-1 is called. + ; A recursive call to a special entry point + ; in the calculator that puts the B register + ; in the system variable BREG. The return + ; address is the next location and where + ; the calculator will expect its first + ; instruction - now pointed to by HL'. + ; The previous pointer to the series of + ; five-byte numbers goes on the machine stack. + +; The initialization phase. + + DEFB $31 ;;duplicate x,x + DEFB $0F ;;addition x+x + DEFB $C0 ;;st-mem-0 x+x + DEFB $02 ;;delete . + DEFB $A0 ;;stk-zero 0 + DEFB $C2 ;;st-mem-2 0 + +; a loop is now entered to perform the algebraic calculation for each of +; the numbers in the series + +;; G-LOOP +L3453: DEFB $31 ;;duplicate v,v. + DEFB $E0 ;;get-mem-0 v,v,x+2 + DEFB $04 ;;multiply v,v*x+2 + DEFB $E2 ;;get-mem-2 v,v*x+2,v + DEFB $C1 ;;st-mem-1 + DEFB $03 ;;subtract + DEFB $38 ;;end-calc + +; the previous pointer is fetched from the machine stack to H'L' where it +; addresses one of the numbers of the series following the series literal. + + CALL L33C6 ; routine STK-DATA is called directly to + ; push a value and advance H'L'. + CALL L3362 ; routine GEN-ENT-2 recursively re-enters + ; the calculator without disturbing + ; system variable BREG + ; H'L' value goes on the machine stack and is + ; then loaded as usual with the next address. + + DEFB $0F ;;addition + DEFB $01 ;;exchange + DEFB $C2 ;;st-mem-2 + DEFB $02 ;;delete + + DEFB $35 ;;dec-jr-nz + DEFB $EE ;;back to L3453, G-LOOP + +; when the counted loop is complete the final subtraction yields the result +; for example SIN X. + + DEFB $E1 ;;get-mem-1 + DEFB $03 ;;subtract + DEFB $38 ;;end-calc + + RET ; return with H'L' pointing to location + ; after last number in series. + +; ----------------------- +; Absolute magnitude (2A) +; ----------------------- +; This calculator literal finds the absolute value of the last value, +; integer or floating point, on calculator stack. + +;; abs +L346A: LD B,$FF ; signal abs + JR L3474 ; forward to NEG-TEST + +; ----------------------- +; Handle unary minus (1B) +; ----------------------- +; Unary so on entry HL points to last value, DE to STKEND. + +;; NEGATE +;; negate +L346E: CALL L34E9 ; call routine TEST-ZERO and + RET C ; return if so leaving zero unchanged. + + LD B,$00 ; signal negate required before joining + ; common code. + +;; NEG-TEST +L3474: LD A,(HL) ; load first byte and + AND A ; test for zero + JR Z,L3483 ; forward to INT-CASE if a small integer + +; for floating point numbers a single bit denotes the sign. + + INC HL ; address the first byte of mantissa. + LD A,B ; action flag $FF=abs, $00=neg. + AND $80 ; now $80 $00 + OR (HL) ; sets bit 7 for abs + RLA ; sets carry for abs and if number negative + CCF ; complement carry flag + RRA ; and rotate back in altering sign + LD (HL),A ; put the altered adjusted number back + DEC HL ; HL points to result + RET ; return with DE unchanged + +; --- + +; for integer numbers an entire byte denotes the sign. + +;; INT-CASE +L3483: PUSH DE ; save STKEND. + + PUSH HL ; save pointer to the last value/result. + + CALL L2D7F ; routine INT-FETCH puts integer in DE + ; and the sign in C. + + POP HL ; restore the result pointer. + + LD A,B ; $FF=abs, $00=neg + OR C ; $FF for abs, no change neg + CPL ; $00 for abs, switched for neg + LD C,A ; transfer result to sign byte. + + CALL L2D8E ; routine INT-STORE to re-write the integer. + + POP DE ; restore STKEND. + RET ; return. + +; ----------- +; Signum (29) +; ----------- +; This routine replaces the last value on the calculator stack, +; which may be in floating point or integer form, with the integer values +; zero if zero, with one if positive and with -minus one if negative. + +;; sgn +L3492: CALL L34E9 ; call routine TEST-ZERO and + RET C ; exit if so as no change is required. + + PUSH DE ; save pointer to STKEND. + + LD DE,$0001 ; the result will be 1. + INC HL ; skip over the exponent. + RL (HL) ; rotate the sign bit into the carry flag. + DEC HL ; step back to point to the result. + SBC A,A ; byte will be $FF if negative, $00 if positive. + LD C,A ; store the sign byte in the C register. + CALL L2D8E ; routine INT-STORE to overwrite the last + ; value with 0001 and sign. + + POP DE ; restore STKEND. + RET ; return. + +; ----------------------- +; Handle IN function (2C) +; ----------------------- +; This function reads a byte from an input port. + +;; in +L34A5: CALL L1E99 ; routine FIND-INT2 puts port address in BC. + ; all 16 bits are put on the address line. + IN A,(C) ; read the port. + + JR L34B0 ; exit to STACK-A (via IN-PK-STK to save a byte + ; of instruction code). + +; ------------------------- +; Handle PEEK function (2B) +; ------------------------- +; This function returns the contents of a memory address. +; The entire address space can be peeked including the ROM. + +;; peek +L34AC: CALL L1E99 ; routine FIND-INT2 puts address in BC. + LD A,(BC) ; load contents into A register. + +;; IN-PK-STK +L34B0: JP L2D28 ; exit via STACK-A to put value on the + ; calculator stack. + +; --------------- +; USR number (2D) +; --------------- +; The USR function followed by a number 0-65535 is the method by which +; the Spectrum invokes machine code programs. This function returns the +; contents of the BC register pair. +; Note. that STACK-BC re-initializes the IY register if a user-written +; program has altered it. + +;; usr-no +L34B3: CALL L1E99 ; routine FIND-INT2 to fetch the + ; supplied address into BC. + + LD HL,L2D2B ; address: STACK-BC is + PUSH HL ; pushed onto the machine stack. + PUSH BC ; then the address of the machine code + ; routine. + + RET ; make an indirect jump to the routine + ; and, hopefully, to STACK-BC also. + +; --------------- +; USR string (19) +; --------------- +; The user function with a one-character string argument, calculates the +; address of the User Defined Graphic character that is in the string. +; As an alternative, the ASCII equivalent, upper or lower case, +; may be supplied. This provides a user-friendly method of redefining +; the 21 User Definable Graphics e.g. +; POKE USR "a", BIN 10000000 will put a dot in the top left corner of the +; character 144. +; Note. the curious double check on the range. With 26 UDGs the first check +; only is necessary. With anything less the second check only is required. +; It is highly likely that the first check was written by Steven Vickers. + +;; usr-$ +L34BC: CALL L2BF1 ; routine STK-FETCH fetches the string + ; parameters. + DEC BC ; decrease BC by + LD A,B ; one to test + OR C ; the length. + JR NZ,L34E7 ; to REPORT-A if not a single character. + + LD A,(DE) ; fetch the character + CALL L2C8D ; routine ALPHA sets carry if 'A-Z' or 'a-z'. + JR C,L34D3 ; forward to USR-RANGE if ASCII. + + SUB $90 ; make udgs range 0-20d + JR C,L34E7 ; to REPORT-A if too low. e.g. usr " ". + + CP $15 ; Note. this test is not necessary. + JR NC,L34E7 ; to REPORT-A if higher than 20. + + INC A ; make range 1-21d to match LSBs of ASCII + +;; USR-RANGE +L34D3: DEC A ; make range of bits 0-4 start at zero + ADD A,A ; multiply by eight + ADD A,A ; and lose any set bits + ADD A,A ; range now 0 - 25*8 + CP $A8 ; compare to 21*8 + JR NC,L34E7 ; to REPORT-A if originally higher + ; than 'U','u' or graphics U. + + LD BC,($5C7B) ; fetch the UDG system variable value. + ADD A,C ; add the offset to character + LD C,A ; and store back in register C. + JR NC,L34E4 ; forward to USR-STACK if no overflow. + + INC B ; increment high byte. + +;; USR-STACK +L34E4: JP L2D2B ; jump back and exit via STACK-BC to store + +; --- + +;; REPORT-A +L34E7: RST 08H ; ERROR-1 + DEFB $09 ; Error Report: Invalid argument + +; ------------- +; Test for zero +; ------------- +; Test if top value on calculator stack is zero. +; The carry flag is set if the last value is zero but no registers are altered. +; All five bytes will be zero but first four only need be tested. +; On entry HL points to the exponent the first byte of the value. + +;; TEST-ZERO +L34E9: PUSH HL ; preserve HL which is used to address. + PUSH BC ; preserve BC which is used as a store. + LD B,A ; preserve A in B. + + LD A,(HL) ; load first byte to accumulator + INC HL ; advance. + OR (HL) ; OR with second byte and clear carry. + INC HL ; advance. + OR (HL) ; OR with third byte. + INC HL ; advance. + OR (HL) ; OR with fourth byte. + + LD A,B ; restore A without affecting flags. + POP BC ; restore the saved + POP HL ; registers. + + RET NZ ; return if not zero and with carry reset. + + SCF ; set the carry flag. + RET ; return with carry set if zero. + +; ----------------------- +; Greater than zero ($37) +; ----------------------- +; Test if the last value on the calculator stack is greater than zero. +; This routine is also called directly from the end-tests of the comparison +; routine. + +;; GREATER-0 +;; greater-0 +L34F9: CALL L34E9 ; routine TEST-ZERO + RET C ; return if was zero as this + ; is also the Boolean 'false' value. + + LD A,$FF ; prepare XOR mask for sign bit + JR L3507 ; forward to SIGN-TO-C + ; to put sign in carry + ; (carry will become set if sign is positive) + ; and then overwrite location with 1 or 0 + ; as appropriate. + +; ------------------------ +; Handle NOT operator ($30) +; ------------------------ +; This overwrites the last value with 1 if it was zero else with zero +; if it was any other value. +; +; e.g NOT 0 returns 1, NOT 1 returns 0, NOT -3 returns 0. +; +; The subroutine is also called directly from the end-tests of the comparison +; operator. + +;; NOT +;; not +L3501: CALL L34E9 ; routine TEST-ZERO sets carry if zero + + JR L350B ; to FP-0/1 to overwrite operand with + ; 1 if carry is set else to overwrite with zero. + +; ------------------- +; Less than zero (36) +; ------------------- +; Destructively test if last value on calculator stack is less than zero. +; Bit 7 of second byte will be set if so. + +;; less-0 +L3506: XOR A ; set xor mask to zero + ; (carry will become set if sign is negative). + +; transfer sign of mantissa to Carry Flag. + +;; SIGN-TO-C +L3507: INC HL ; address 2nd byte. + XOR (HL) ; bit 7 of HL will be set if number is negative. + DEC HL ; address 1st byte again. + RLCA ; rotate bit 7 of A to carry. + +; ----------- +; Zero or one +; ----------- +; This routine places an integer value of zero or one at the addressed location +; of the calculator stack or MEM area. The value one is written if carry is +; set on entry else zero. + +;; FP-0/1 +L350B: PUSH HL ; save pointer to the first byte + LD A,$00 ; load accumulator with zero - without + ; disturbing flags. + LD (HL),A ; zero to first byte + INC HL ; address next + LD (HL),A ; zero to 2nd byte + INC HL ; address low byte of integer + RLA ; carry to bit 0 of A + LD (HL),A ; load one or zero to low byte. + RRA ; restore zero to accumulator. + INC HL ; address high byte of integer. + LD (HL),A ; put a zero there. + INC HL ; address fifth byte. + LD (HL),A ; put a zero there. + POP HL ; restore pointer to the first byte. + RET ; return. + +; ----------------------- +; Handle OR operator (07) +; ----------------------- +; The Boolean OR operator. eg. X OR Y +; The result is zero if both values are zero else a non-zero value. +; +; e.g. 0 OR 0 returns 0. +; -3 OR 0 returns -3. +; 0 OR -3 returns 1. +; -3 OR 2 returns 1. +; +; A binary operation. +; On entry HL points to first operand (X) and DE to second operand (Y). + +;; or +L351B: EX DE,HL ; make HL point to second number + CALL L34E9 ; routine TEST-ZERO + EX DE,HL ; restore pointers + RET C ; return if result was zero - first operand, + ; now the last value, is the result. + + SCF ; set carry flag + JR L350B ; back to FP-0/1 to overwrite the first operand + ; with the value 1. + + +; ----------------------------- +; Handle number AND number (08) +; ----------------------------- +; The Boolean AND operator. +; +; e.g. -3 AND 2 returns -3. +; -3 AND 0 returns 0. +; 0 and -2 returns 0. +; 0 and 0 returns 0. +; +; Compare with OR routine above. + +;; no-&-no +L3524: EX DE,HL ; make HL address second operand. + + CALL L34E9 ; routine TEST-ZERO sets carry if zero. + + EX DE,HL ; restore pointers. + RET NC ; return if second non-zero, first is result. + +; + + AND A ; else clear carry. + JR L350B ; back to FP-0/1 to overwrite first operand + ; with zero for return value. + +; ----------------------------- +; Handle string AND number (10) +; ----------------------------- +; e.g. "You Win" AND score>99 will return the string if condition is true +; or the null string if false. + +;; str-&-no +L352D: EX DE,HL ; make HL point to the number. + CALL L34E9 ; routine TEST-ZERO. + EX DE,HL ; restore pointers. + RET NC ; return if number was not zero - the string + ; is the result. + +; if the number was zero (false) then the null string must be returned by +; altering the length of the string on the calculator stack to zero. + + PUSH DE ; save pointer to the now obsolete number + ; (which will become the new STKEND) + + DEC DE ; point to the 5th byte of string descriptor. + XOR A ; clear the accumulator. + LD (DE),A ; place zero in high byte of length. + DEC DE ; address low byte of length. + LD (DE),A ; place zero there - now the null string. + + POP DE ; restore pointer - new STKEND. + RET ; return. + +; ----------------------------------- +; Perform comparison ($09-$0E, $11-$16) +; ----------------------------------- +; True binary operations. +; +; A single entry point is used to evaluate six numeric and six string +; comparisons. On entry, the calculator literal is in the B register and +; the two numeric values, or the two string parameters, are on the +; calculator stack. +; The individual bits of the literal are manipulated to group similar +; operations although the SUB 8 instruction does nothing useful and merely +; alters the string test bit. +; Numbers are compared by subtracting one from the other, strings are +; compared by comparing every character until a mismatch, or the end of one +; or both, is reached. +; +; Numeric Comparisons. +; -------------------- +; The 'x>y' example is the easiest as it employs straight-thru logic. +; Number y is subtracted from x and the result tested for greater-0 yielding +; a final value 1 (true) or 0 (false). +; For 'x0? NOT +; no-gr-eql x>=y 0A 00000010 dec 00000001 10000000c swap y-x ? --- >0? NOT +; nos-neql x<>y 0B 00000011 dec 00000010 00000001 ---- x-y ? NOT --- NOT +; no-grtr x>y 0C 00000100 - 00000100 00000010 ---- x-y ? --- >0? --- +; no-less x0? --- +; nos-eql x=y 0E 00000110 - 00000110 00000011 ---- x-y ? NOT --- --- +; +; comp -> C/F +; ==== === +; str-l-eql x$<=y$ 11 00001001 dec 00001000 00000100 ---- x$y$ 0 !or >0? NOT +; str-gr-eql x$>=y$ 12 00001010 dec 00001001 10000100c swap y$x$ 0 !or >0? NOT +; strs-neql x$<>y$ 13 00001011 dec 00001010 00000101 ---- x$y$ 0 !or >0? NOT +; str-grtr x$>y$ 14 00001100 - 00001100 00000110 ---- x$y$ 0 !or >0? --- +; str-less x$0? --- +; strs-eql x$=y$ 16 00001110 - 00001110 00000111 ---- x$y$ 0 !or >0? --- +; +; String comparisons are a little different in that the eql/neql carry flag +; from the 2nd RRCA is, as before, fed into the first of the end tests but +; along the way it gets modified by the comparison process. The result on the +; stack always starts off as zero and the carry fed in determines if NOT is +; applied to it. So the only time the greater-0 test is applied is if the +; stack holds zero which is not very efficient as the test will always yield +; zero. The most likely explanation is that there were once separate end tests +; for numbers and strings. + +;; no-l-eql, etc. +L353B: LD A,B ; transfer literal to accumulator. + SUB $08 ; subtract eight - which is not useful. + + BIT 2,A ; isolate '>', '<', '='. + + JR NZ,L3543 ; skip to EX-OR-NOT with these. + + DEC A ; else make $00-$02, $08-$0A to match bits 0-2. + +;; EX-OR-NOT +L3543: RRCA ; the first RRCA sets carry for a swap. + JR NC,L354E ; forward to NU-OR-STR with other 8 cases + +; for the other 4 cases the two values on the calculator stack are exchanged. + + PUSH AF ; save A and carry. + PUSH HL ; save HL - pointer to first operand. + ; (DE points to second operand). + + CALL L343C ; routine exchange swaps the two values. + ; (HL = second operand, DE = STKEND) + + POP DE ; DE = first operand + EX DE,HL ; as we were. + POP AF ; restore A and carry. + +; Note. it would be better if the 2nd RRCA preceded the string test. +; It would save two duplicate bytes and if we also got rid of that sub 8 +; at the beginning we wouldn't have to alter which bit we test. + +;; NU-OR-STR +L354E: BIT 2,A ; test if a string comparison. + JR NZ,L3559 ; forward to STRINGS if so. + +; continue with numeric comparisons. + + RRCA ; 2nd RRCA causes eql/neql to set carry. + PUSH AF ; save A and carry + + CALL L300F ; routine subtract leaves result on stack. + JR L358C ; forward to END-TESTS + +; --- + +;; STRINGS +L3559: RRCA ; 2nd RRCA causes eql/neql to set carry. + PUSH AF ; save A and carry. + + CALL L2BF1 ; routine STK-FETCH gets 2nd string params + PUSH DE ; save start2 *. + PUSH BC ; and the length. + + CALL L2BF1 ; routine STK-FETCH gets 1st string + ; parameters - start in DE, length in BC. + POP HL ; restore length of second to HL. + +; A loop is now entered to compare, by subtraction, each corresponding character +; of the strings. For each successful match, the pointers are incremented and +; the lengths decreased and the branch taken back to here. If both string +; remainders become null at the same time, then an exact match exists. + +;; BYTE-COMP +L3564: LD A,H ; test if the second string + OR L ; is the null string and hold flags. + + EX (SP),HL ; put length2 on stack, bring start2 to HL *. + LD A,B ; hi byte of length1 to A + + JR NZ,L3575 ; forward to SEC-PLUS if second not null. + + OR C ; test length of first string. + +;; SECND-LOW +L356B: POP BC ; pop the second length off stack. + JR Z,L3572 ; forward to BOTH-NULL if first string is also + ; of zero length. + +; the true condition - first is longer than second (SECND-LESS) + + POP AF ; restore carry (set if eql/neql) + CCF ; complement carry flag. + ; Note. equality becomes false. + ; Inequality is true. By swapping or applying + ; a terminal 'not', all comparisons have been + ; manipulated so that this is success path. + JR L3588 ; forward to leave via STR-TEST + +; --- +; the branch was here with a match + +;; BOTH-NULL +L3572: POP AF ; restore carry - set for eql/neql + JR L3588 ; forward to STR-TEST + +; --- +; the branch was here when 2nd string not null and low byte of first is yet +; to be tested. + + +;; SEC-PLUS +L3575: OR C ; test the length of first string. + JR Z,L3585 ; forward to FRST-LESS if length is zero. + +; both strings have at least one character left. + + LD A,(DE) ; fetch character of first string. + SUB (HL) ; subtract with that of 2nd string. + JR C,L3585 ; forward to FRST-LESS if carry set + + JR NZ,L356B ; back to SECND-LOW and then STR-TEST + ; if not exact match. + + DEC BC ; decrease length of 1st string. + INC DE ; increment 1st string pointer. + + INC HL ; increment 2nd string pointer. + EX (SP),HL ; swap with length on stack + DEC HL ; decrement 2nd string length + JR L3564 ; back to BYTE-COMP + +; --- +; the false condition. + +;; FRST-LESS +L3585: POP BC ; discard length + POP AF ; pop A + AND A ; clear the carry for false result. + +; --- +; exact match and x$>y$ rejoin here + +;; STR-TEST +L3588: PUSH AF ; save A and carry + + RST 28H ;; FP-CALC + DEFB $A0 ;;stk-zero an initial false value. + DEFB $38 ;;end-calc + +; both numeric and string paths converge here. + +;; END-TESTS +L358C: POP AF ; pop carry - will be set if eql/neql + PUSH AF ; save it again. + + CALL C,L3501 ; routine NOT sets true(1) if equal(0) + ; or, for strings, applies true result. + + POP AF ; pop carry and + PUSH AF ; save A + + CALL NC,L34F9 ; routine GREATER-0 tests numeric subtraction + ; result but also needlessly tests the string + ; value for zero - it must be. + + POP AF ; pop A + RRCA ; the third RRCA - test for '<=', '>=' or '<>'. + CALL NC,L3501 ; apply a terminal NOT if so. + RET ; return. + +; ------------------------- +; String concatenation ($17) +; ------------------------- +; This literal combines two strings into one e.g. LET a$ = b$ + c$ +; The two parameters of the two strings to be combined are on the stack. + +;; strs-add +L359C: CALL L2BF1 ; routine STK-FETCH fetches string parameters + ; and deletes calculator stack entry. + PUSH DE ; save start address. + PUSH BC ; and length. + + CALL L2BF1 ; routine STK-FETCH for first string + POP HL ; re-fetch first length + PUSH HL ; and save again + PUSH DE ; save start of second string + PUSH BC ; and its length. + + ADD HL,BC ; add the two lengths. + LD B,H ; transfer to BC + LD C,L ; and create + RST 30H ; BC-SPACES in workspace. + ; DE points to start of space. + + CALL L2AB2 ; routine STK-STO-$ stores parameters + ; of new string updating STKEND. + + POP BC ; length of first + POP HL ; address of start + LD A,B ; test for + OR C ; zero length. + JR Z,L35B7 ; to OTHER-STR if null string + + LDIR ; copy string to workspace. + +;; OTHER-STR +L35B7: POP BC ; now second length + POP HL ; and start of string + LD A,B ; test this one + OR C ; for zero length + JR Z,L35BF ; skip forward to STK-PNTRS if so as complete. + + LDIR ; else copy the bytes. + ; and continue into next routine which + ; sets the calculator stack pointers. + +; -------------------- +; Check stack pointers +; -------------------- +; Register DE is set to STKEND and HL, the result pointer, is set to five +; locations below this. +; This routine is used when it is inconvenient to save these values at the +; time the calculator stack is manipulated due to other activity on the +; machine stack. +; This routine is also used to terminate the VAL and READ-IN routines for +; the same reason and to initialize the calculator stack at the start of +; the CALCULATE routine. + +;; STK-PNTRS +L35BF: LD HL,($5C65) ; fetch STKEND value from system variable. + LD DE,$FFFB ; the value -5 + PUSH HL ; push STKEND value. + + ADD HL,DE ; subtract 5 from HL. + + POP DE ; pop STKEND to DE. + RET ; return. + +; ---------------- +; Handle CHR$ (2F) +; ---------------- +; This function returns a single character string that is a result of +; converting a number in the range 0-255 to a string e.g. CHR$ 65 = "A". + +;; chrs +L35C9: CALL L2DD5 ; routine FP-TO-A puts the number in A. + + JR C,L35DC ; forward to REPORT-Bd if overflow + JR NZ,L35DC ; forward to REPORT-Bd if negative + + PUSH AF ; save the argument. + + LD BC,$0001 ; one space required. + RST 30H ; BC-SPACES makes DE point to start + + POP AF ; restore the number. + + LD (DE),A ; and store in workspace + + CALL L2AB2 ; routine STK-STO-$ stacks descriptor. + + EX DE,HL ; make HL point to result and DE to STKEND. + RET ; return. + +; --- + +;; REPORT-Bd +L35DC: RST 08H ; ERROR-1 + DEFB $0A ; Error Report: Integer out of range + +; ---------------------------- +; Handle VAL and VAL$ ($1D, $18) +; ---------------------------- +; VAL treats the characters in a string as a numeric expression. +; e.g. VAL "2.3" = 2.3, VAL "2+4" = 6, VAL ("2" + "4") = 24. +; VAL$ treats the characters in a string as a string expression. +; e.g. VAL$ (z$+"(2)") = a$(2) if z$ happens to be "a$". + +;; val +;; val$ +L35DE: LD HL,($5C5D) ; fetch value of system variable CH_ADD + PUSH HL ; and save on the machine stack. + LD A,B ; fetch the literal (either $1D or $18). + ADD A,$E3 ; add $E3 to form $00 (setting carry) or $FB. + SBC A,A ; now form $FF bit 6 = numeric result + ; or $00 bit 6 = string result. + PUSH AF ; save this mask on the stack + + CALL L2BF1 ; routine STK-FETCH fetches the string operand + ; from calculator stack. + + PUSH DE ; save the address of the start of the string. + INC BC ; increment the length for a carriage return. + + RST 30H ; BC-SPACES creates the space in workspace. + POP HL ; restore start of string to HL. + LD ($5C5D),DE ; load CH_ADD with start DE in workspace. + + PUSH DE ; save the start in workspace + LDIR ; copy string from program or variables or + ; workspace to the workspace area. + EX DE,HL ; end of string + 1 to HL + DEC HL ; decrement HL to point to end of new area. + LD (HL),$0D ; insert a carriage return at end. + RES 7,(IY+$01) ; update FLAGS - signal checking syntax. + CALL L24FB ; routine SCANNING evaluates string + ; expression and result. + + RST 18H ; GET-CHAR fetches next character. + CP $0D ; is it the expected carriage return ? + JR NZ,L360C ; forward to V-RPORT-C if not + ; 'Nonsense in BASIC'. + + POP HL ; restore start of string in workspace. + POP AF ; restore expected result flag (bit 6). + XOR (IY+$01) ; xor with FLAGS now updated by SCANNING. + AND $40 ; test bit 6 - should be zero if result types + ; match. + +;; V-RPORT-C +L360C: JP NZ,L1C8A ; jump back to REPORT-C with a result mismatch. + + LD ($5C5D),HL ; set CH_ADD to the start of the string again. + SET 7,(IY+$01) ; update FLAGS - signal running program. + CALL L24FB ; routine SCANNING evaluates the string + ; in full leaving result on calculator stack. + + POP HL ; restore saved character address in program. + LD ($5C5D),HL ; and reset the system variable CH_ADD. + + JR L35BF ; back to exit via STK-PNTRS. + ; resetting the calculator stack pointers + ; HL and DE from STKEND as it wasn't possible + ; to preserve them during this routine. + +; ---------------- +; Handle STR$ (2E) +; ---------------- +; +; + +;; str$ +L361F: LD BC,$0001 ; create an initial byte in workspace + RST 30H ; using BC-SPACES restart. + + LD ($5C5B),HL ; set system variable K_CUR to new location. + PUSH HL ; and save start on machine stack also. + + LD HL,($5C51) ; fetch value of system variable CURCHL + PUSH HL ; and save that too. + + LD A,$FF ; select system channel 'R'. + CALL L1601 ; routine CHAN-OPEN opens it. + CALL L2DE3 ; routine PRINT-FP outputs the number to + ; workspace updating K-CUR. + + POP HL ; restore current channel. + CALL L1615 ; routine CHAN-FLAG resets flags. + + POP DE ; fetch saved start of string to DE. + LD HL,($5C5B) ; load HL with end of string from K_CUR. + + AND A ; prepare for true subtraction. + SBC HL,DE ; subtract start from end to give length. + LD B,H ; transfer the length to + LD C,L ; the BC register pair. + + CALL L2AB2 ; routine STK-STO-$ stores string parameters + ; on the calculator stack. + + EX DE,HL ; HL = last value, DE = STKEND. + RET ; return. + +; ------------ +; Read-in (1A) +; ------------ +; This is the calculator literal used by the INKEY$ function when a '#' +; is encountered after the keyword. +; INKEY$ # does not interact correctly with the keyboard, #0 or #1, and +; its uses are for other channels. + +;; read-in +L3645: CALL L1E94 ; routine FIND-INT1 fetches stream to A + CP $10 ; compare with 16 decimal. + JP NC,L1E9F ; jump to REPORT-Bb if not in range 0 - 15. + ; 'Integer out of range' + ; (REPORT-Bd is within range) + + LD HL,($5C51) ; fetch current channel CURCHL + PUSH HL ; save it + CALL L1601 ; routine CHAN-OPEN opens channel + + CALL L15E6 ; routine INPUT-AD - the channel must have an + ; input stream or else error here from stream + ; stub. + LD BC,$0000 ; initialize length of string to zero + JR NC,L365F ; forward to R-I-STORE if no key detected. + + INC C ; increase length to one. + + RST 30H ; BC-SPACES creates space for one character + ; in workspace. + LD (DE),A ; the character is inserted. + +;; R-I-STORE +L365F: CALL L2AB2 ; routine STK-STO-$ stacks the string + ; parameters. + POP HL ; restore current channel address + CALL L1615 ; routine CHAN-FLAG resets current channel + ; system variable and flags. + JP L35BF ; jump back to STK-PNTRS + +; ---------------- +; Handle CODE (1C) +; ---------------- +; Returns the ASCII code of a character or first character of a string +; e.g. CODE "Aardvark" = 65, CODE "" = 0. + +;; code +L3669: CALL L2BF1 ; routine STK-FETCH to fetch and delete the + ; string parameters. + ; DE points to the start, BC holds the length. + LD A,B ; test length + OR C ; of the string. + JR Z,L3671 ; skip to STK-CODE with zero if the null string. + + LD A,(DE) ; else fetch the first character. + +;; STK-CODE +L3671: JP L2D28 ; jump back to STACK-A (with memory check) + +; --------------- +; Handle LEN (1E) +; --------------- +; Returns the length of a string. +; In Sinclair BASIC strings can be more than twenty thousand characters long +; so a sixteen-bit register is required to store the length + +;; len +L3674: CALL L2BF1 ; routine STK-FETCH to fetch and delete the + ; string parameters from the calculator stack. + ; register BC now holds the length of string. + + JP L2D2B ; jump back to STACK-BC to save result on the + ; calculator stack (with memory check). + +; ------------------------- +; Decrease the counter (35) +; ------------------------- +; The calculator has an instruction that decrements a single-byte +; pseudo-register and makes consequential relative jumps just like +; the Z80's DJNZ instruction. + +;; dec-jr-nz +L367A: EXX ; switch in set that addresses code + + PUSH HL ; save pointer to offset byte + LD HL,$5C67 ; address BREG in system variables + DEC (HL) ; decrement it + POP HL ; restore pointer + + JR NZ,L3687 ; to JUMP-2 if not zero + + INC HL ; step past the jump length. + EXX ; switch in the main set. + RET ; return. + +; Note. as a general rule the calculator avoids using the IY register +; otherwise the cumbersome 4 instructions in the middle could be replaced by +; dec (iy+$2d) - three bytes instead of six. + + +; --------- +; Jump (33) +; --------- +; This enables the calculator to perform relative jumps just like +; the Z80 chip's JR instruction + +;; jump +;; JUMP +L3686: EXX ;switch in pointer set + +;; JUMP-2 +L3687: LD E,(HL) ; the jump byte 0-127 forward, 128-255 back. + LD A,E ; transfer to accumulator. + RLA ; if backward jump, carry is set. + SBC A,A ; will be $FF if backward or $00 if forward. + LD D,A ; transfer to high byte. + ADD HL,DE ; advance calculator pointer forward or back. + EXX ; switch back. + RET ; return. + +; ----------------- +; Jump on true (00) +; ----------------- +; This enables the calculator to perform conditional relative jumps +; dependent on whether the last test gave a true result + +;; jump-true +L368F: INC DE ; collect the + INC DE ; third byte + LD A,(DE) ; of the test + DEC DE ; result and + DEC DE ; backtrack. + + AND A ; is result 0 or 1 ? + JR NZ,L3686 ; back to JUMP if true (1). + + EXX ; else switch in the pointer set. + INC HL ; step past the jump length. + EXX ; switch in the main set. + RET ; return. + +; ----------------------- +; End of calculation (38) +; ----------------------- +; The end-calc literal terminates a mini-program written in the Spectrum's +; internal language. + +;; end-calc +L369B: POP AF ; drop the calculator return address RE-ENTRY + EXX ; switch to the other set. + + EX (SP),HL ; transfer H'L' to machine stack for the + ; return address. + ; when exiting recursion then the previous + ; pointer is transferred to H'L'. + + EXX ; back to main set. + RET ; return. + + +; ------------------------ +; THE 'MODULUS' SUBROUTINE +; ------------------------ +; (offset: $32 'n-mod-m') +; +; + +;; n-mod-m +L36A0: RST 28H ;; FP-CALC 17, 3. + DEFB $C0 ;;st-mem-0 17, 3. + DEFB $02 ;;delete 17. + DEFB $31 ;;duplicate 17, 17. + DEFB $E0 ;;get-mem-0 17, 17, 3. + DEFB $05 ;;division 17, 17/3. + DEFB $27 ;;int 17, 5. + DEFB $E0 ;;get-mem-0 17, 5, 3. + DEFB $01 ;;exchange 17, 3, 5. + DEFB $C0 ;;st-mem-0 17, 3, 5. + DEFB $04 ;;multiply 17, 15. + DEFB $03 ;;subtract 2. + DEFB $E0 ;;get-mem-0 2, 5. + DEFB $38 ;;end-calc 2, 5. + + RET ; return. + + +; ------------------ +; THE 'INT' FUNCTION +; ------------------ +; (offset $27: 'int' ) +; +; This function returns the integer of x, which is just the same as truncate +; for positive numbers. The truncate literal truncates negative numbers +; upwards so that -3.4 gives -3 whereas the BASIC INT function has to +; truncate negative numbers down so that INT -3.4 is -4. +; It is best to work through using, say, +-3.4 as examples. + +;; int +L36AF: RST 28H ;; FP-CALC x. (= 3.4 or -3.4). + DEFB $31 ;;duplicate x, x. + DEFB $36 ;;less-0 x, (1/0) + DEFB $00 ;;jump-true x, (1/0) + DEFB $04 ;;to L36B7, X-NEG + + DEFB $3A ;;truncate trunc 3.4 = 3. + DEFB $38 ;;end-calc 3. + + RET ; return with + int x on stack. + +; --- + + +;; X-NEG +L36B7: DEFB $31 ;;duplicate -3.4, -3.4. + DEFB $3A ;;truncate -3.4, -3. + DEFB $C0 ;;st-mem-0 -3.4, -3. + DEFB $03 ;;subtract -.4 + DEFB $E0 ;;get-mem-0 -.4, -3. + DEFB $01 ;;exchange -3, -.4. + DEFB $30 ;;not -3, (0). + DEFB $00 ;;jump-true -3. + DEFB $03 ;;to L36C2, EXIT -3. + + DEFB $A1 ;;stk-one -3, 1. + DEFB $03 ;;subtract -4. + +;; EXIT +L36C2: DEFB $38 ;;end-calc -4. + + RET ; return. + + +; ---------------- +; Exponential (26) +; ---------------- +; +; + +;; EXP +;; exp +L36C4: RST 28H ;; FP-CALC + DEFB $3D ;;re-stack + DEFB $34 ;;stk-data + DEFB $F1 ;;Exponent: $81, Bytes: 4 + DEFB $38,$AA,$3B,$29 ;; + DEFB $04 ;;multiply + DEFB $31 ;;duplicate + DEFB $27 ;;int + DEFB $C3 ;;st-mem-3 + DEFB $03 ;;subtract + DEFB $31 ;;duplicate + DEFB $0F ;;addition + DEFB $A1 ;;stk-one + DEFB $03 ;;subtract + DEFB $88 ;;series-08 + DEFB $13 ;;Exponent: $63, Bytes: 1 + DEFB $36 ;;(+00,+00,+00) + DEFB $58 ;;Exponent: $68, Bytes: 2 + DEFB $65,$66 ;;(+00,+00) + DEFB $9D ;;Exponent: $6D, Bytes: 3 + DEFB $78,$65,$40 ;;(+00) + DEFB $A2 ;;Exponent: $72, Bytes: 3 + DEFB $60,$32,$C9 ;;(+00) + DEFB $E7 ;;Exponent: $77, Bytes: 4 + DEFB $21,$F7,$AF,$24 ;; + DEFB $EB ;;Exponent: $7B, Bytes: 4 + DEFB $2F,$B0,$B0,$14 ;; + DEFB $EE ;;Exponent: $7E, Bytes: 4 + DEFB $7E,$BB,$94,$58 ;; + DEFB $F1 ;;Exponent: $81, Bytes: 4 + DEFB $3A,$7E,$F8,$CF ;; + DEFB $E3 ;;get-mem-3 + DEFB $38 ;;end-calc + + CALL L2DD5 ; routine FP-TO-A + JR NZ,L3705 ; to N-NEGTV + + JR C,L3703 ; to REPORT-6b + + ADD A,(HL) ; + JR NC,L370C ; to RESULT-OK + + +;; REPORT-6b +L3703: RST 08H ; ERROR-1 + DEFB $05 ; Error Report: Number too big + +;; N-NEGTV +L3705: JR C,L370E ; to RSLT-ZERO + + SUB (HL) ; + JR NC,L370E ; to RSLT-ZERO + + NEG ; Negate + +;; RESULT-OK +L370C: LD (HL),A ; + RET ; return. + +; --- + + +;; RSLT-ZERO +L370E: RST 28H ;; FP-CALC + DEFB $02 ;;delete + DEFB $A0 ;;stk-zero + DEFB $38 ;;end-calc + + RET ; return. + + +; ---------------------- +; Natural logarithm (25) +; ---------------------- +; +; + +;; ln +L3713: RST 28H ;; FP-CALC + DEFB $3D ;;re-stack + DEFB $31 ;;duplicate + DEFB $37 ;;greater-0 + DEFB $00 ;;jump-true + DEFB $04 ;;to L371C, VALID + + DEFB $38 ;;end-calc + + +;; REPORT-Ab +L371A: RST 08H ; ERROR-1 + DEFB $09 ; Error Report: Invalid argument + +;; VALID +L371C: DEFB $A0 ;;stk-zero + DEFB $02 ;;delete + DEFB $38 ;;end-calc + LD A,(HL) ; + + LD (HL),$80 ; + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC + DEFB $34 ;;stk-data + DEFB $38 ;;Exponent: $88, Bytes: 1 + DEFB $00 ;;(+00,+00,+00) + DEFB $03 ;;subtract + DEFB $01 ;;exchange + DEFB $31 ;;duplicate + DEFB $34 ;;stk-data + DEFB $F0 ;;Exponent: $80, Bytes: 4 + DEFB $4C,$CC,$CC,$CD ;; + DEFB $03 ;;subtract + DEFB $37 ;;greater-0 + DEFB $00 ;;jump-true + DEFB $08 ;;to L373D, GRE.8 + + DEFB $01 ;;exchange + DEFB $A1 ;;stk-one + DEFB $03 ;;subtract + DEFB $01 ;;exchange + DEFB $38 ;;end-calc + + INC (HL) ; + + RST 28H ;; FP-CALC + +;; GRE.8 +L373D: DEFB $01 ;;exchange + DEFB $34 ;;stk-data + DEFB $F0 ;;Exponent: $80, Bytes: 4 + DEFB $31,$72,$17,$F8 ;; + DEFB $04 ;;multiply + DEFB $01 ;;exchange + DEFB $A2 ;;stk-half + DEFB $03 ;;subtract + DEFB $A2 ;;stk-half + DEFB $03 ;;subtract + DEFB $31 ;;duplicate + DEFB $34 ;;stk-data + DEFB $32 ;;Exponent: $82, Bytes: 1 + DEFB $20 ;;(+00,+00,+00) + DEFB $04 ;;multiply + DEFB $A2 ;;stk-half + DEFB $03 ;;subtract + DEFB $8C ;;series-0C + DEFB $11 ;;Exponent: $61, Bytes: 1 + DEFB $AC ;;(+00,+00,+00) + DEFB $14 ;;Exponent: $64, Bytes: 1 + DEFB $09 ;;(+00,+00,+00) + DEFB $56 ;;Exponent: $66, Bytes: 2 + DEFB $DA,$A5 ;;(+00,+00) + DEFB $59 ;;Exponent: $69, Bytes: 2 + DEFB $30,$C5 ;;(+00,+00) + DEFB $5C ;;Exponent: $6C, Bytes: 2 + DEFB $90,$AA ;;(+00,+00) + DEFB $9E ;;Exponent: $6E, Bytes: 3 + DEFB $70,$6F,$61 ;;(+00) + DEFB $A1 ;;Exponent: $71, Bytes: 3 + DEFB $CB,$DA,$96 ;;(+00) + DEFB $A4 ;;Exponent: $74, Bytes: 3 + DEFB $31,$9F,$B4 ;;(+00) + DEFB $E7 ;;Exponent: $77, Bytes: 4 + DEFB $A0,$FE,$5C,$FC ;; + DEFB $EA ;;Exponent: $7A, Bytes: 4 + DEFB $1B,$43,$CA,$36 ;; + DEFB $ED ;;Exponent: $7D, Bytes: 4 + DEFB $A7,$9C,$7E,$5E ;; + DEFB $F0 ;;Exponent: $80, Bytes: 4 + DEFB $6E,$23,$80,$93 ;; + DEFB $04 ;;multiply + DEFB $0F ;;addition + DEFB $38 ;;end-calc + + RET ; return. + + +; ----------------------------- +; THE 'TRIGONOMETRIC' FUNCTIONS +; ----------------------------- +; Trigonometry is rocket science. It is also used by carpenters and pyramid +; builders. +; Some uses can be quite abstract but the principles can be seen in simple +; right-angled triangles. Triangles have some special properties - +; +; 1) The sum of the three angles is always PI radians (180 degrees). +; Very helpful if you know two angles and wish to find the third. +; 2) In any right-angled triangle the sum of the squares of the two shorter +; sides is equal to the square of the longest side opposite the right-angle. +; Very useful if you know the length of two sides and wish to know the +; length of the third side. +; 3) Functions sine, cosine and tangent enable one to calculate the length +; of an unknown side when the length of one other side and an angle is +; known. +; 4) Functions arcsin, arccosine and arctan enable one to calculate an unknown +; angle when the length of two of the sides is known. + +;--------------------------------- +; THE 'REDUCE ARGUMENT' SUBROUTINE +;--------------------------------- +; (offset $39: 'get-argt') +; +; This routine performs two functions on the angle, in radians, that forms +; the argument to the sine and cosine functions. +; First it ensures that the angle 'wraps round'. That if a ship turns through +; an angle of, say, 3*PI radians (540 degrees) then the net effect is to turn +; through an angle of PI radians (180 degrees). +; Secondly it converts the angle in radians to a fraction of a right angle, +; depending within which quadrant the angle lies, with the periodicity +; resembling that of the desired sine value. +; The result lies in the range -1 to +1. +; +; 90 deg. +; +; (pi/2) +; II +1 I +; | +; sin+ |\ | /| sin+ +; cos- | \ | / | cos+ +; tan- | \ | / | tan+ +; | \|/) | +; 180 deg. (pi) 0 -|----+----|-- 0 (0) 0 degrees +; | /|\ | +; sin- | / | \ | sin- +; cos- | / | \ | cos+ +; tan+ |/ | \| tan- +; | +; III -1 IV +; (3pi/2) +; +; 270 deg. +; + +;; get-argt +L3783: RST 28H ;; FP-CALC X. + DEFB $3D ;;re-stack + DEFB $34 ;;stk-data + DEFB $EE ;;Exponent: $7E, + ;;Bytes: 4 + DEFB $22,$F9,$83,$6E ;; X, 1/(2*PI) + DEFB $04 ;;multiply X/(2*PI) = fraction + DEFB $31 ;;duplicate + DEFB $A2 ;;stk-half + DEFB $0F ;;addition + DEFB $27 ;;int + + DEFB $03 ;;subtract now range -.5 to .5 + + DEFB $31 ;;duplicate + DEFB $0F ;;addition now range -1 to 1. + DEFB $31 ;;duplicate + DEFB $0F ;;addition now range -2 to +2. + +; quadrant I (0 to +1) and quadrant IV (-1 to 0) are now correct. +; quadrant II ranges +1 to +2. +; quadrant III ranges -2 to -1. + + DEFB $31 ;;duplicate Y, Y. + DEFB $2A ;;abs Y, abs(Y). range 1 to 2 + DEFB $A1 ;;stk-one Y, abs(Y), 1. + DEFB $03 ;;subtract Y, abs(Y)-1. range 0 to 1 + DEFB $31 ;;duplicate Y, Z, Z. + DEFB $37 ;;greater-0 Y, Z, (1/0). + + DEFB $C0 ;;st-mem-0 store as possible sign + ;; for cosine function. + + DEFB $00 ;;jump-true + DEFB $04 ;;to L37A1, ZPLUS with quadrants II and III. + +; else the angle lies in quadrant I or IV and value Y is already correct. + + DEFB $02 ;;delete Y. delete the test value. + DEFB $38 ;;end-calc Y. + + RET ; return. with Q1 and Q4 >>> + +; --- + +; the branch was here with quadrants II (0 to 1) and III (1 to 0). +; Y will hold -2 to -1 if this is quadrant III. + +;; ZPLUS +L37A1: DEFB $A1 ;;stk-one Y, Z, 1. + DEFB $03 ;;subtract Y, Z-1. Q3 = 0 to -1 + DEFB $01 ;;exchange Z-1, Y. + DEFB $36 ;;less-0 Z-1, (1/0). + DEFB $00 ;;jump-true Z-1. + DEFB $02 ;;to L37A8, YNEG + ;;if angle in quadrant III + +; else angle is within quadrant II (-1 to 0) + + DEFB $1B ;;negate range +1 to 0. + +;; YNEG +L37A8: DEFB $38 ;;end-calc quadrants II and III correct. + + RET ; return. + + +;---------------------- +; THE 'COSINE' FUNCTION +;---------------------- +; (offset $20: 'cos') +; Cosines are calculated as the sine of the opposite angle rectifying the +; sign depending on the quadrant rules. +; +; +; /| +; h /y| +; / |o +; /x | +; /----| +; a +; +; The cosine of angle x is the adjacent side (a) divided by the hypotenuse 1. +; However if we examine angle y then a/h is the sine of that angle. +; Since angle x plus angle y equals a right-angle, we can find angle y by +; subtracting angle x from pi/2. +; However it's just as easy to reduce the argument first and subtract the +; reduced argument from the value 1 (a reduced right-angle). +; It's even easier to subtract 1 from the angle and rectify the sign. +; In fact, after reducing the argument, the absolute value of the argument +; is used and rectified using the test result stored in mem-0 by 'get-argt' +; for that purpose. +; + +;; cos +L37AA: RST 28H ;; FP-CALC angle in radians. + DEFB $39 ;;get-argt X reduce -1 to +1 + + DEFB $2A ;;abs ABS X. 0 to 1 + DEFB $A1 ;;stk-one ABS X, 1. + DEFB $03 ;;subtract now opposite angle + ;; although sign is -ve. + + DEFB $E0 ;;get-mem-0 fetch the sign indicator + DEFB $00 ;;jump-true + DEFB $06 ;;fwd to L37B7, C-ENT + ;;forward to common code if in QII or QIII. + + DEFB $1B ;;negate else make sign +ve. + DEFB $33 ;;jump + DEFB $03 ;;fwd to L37B7, C-ENT + ;; with quadrants I and IV. + +;-------------------- +; THE 'SINE' FUNCTION +;-------------------- +; (offset $1F: 'sin') +; This is a fundamental transcendental function from which others such as cos +; and tan are directly, or indirectly, derived. +; It uses the series generator to produce Chebyshev polynomials. +; +; +; /| +; 1 / | +; / |x +; /a | +; /----| +; y +; +; The 'get-argt' function is designed to modify the angle and its sign +; in line with the desired sine value and afterwards it can launch straight +; into common code. + +;; sin +L37B5: RST 28H ;; FP-CALC angle in radians + DEFB $39 ;;get-argt reduce - sign now correct. + +;; C-ENT +L37B7: DEFB $31 ;;duplicate + DEFB $31 ;;duplicate + DEFB $04 ;;multiply + DEFB $31 ;;duplicate + DEFB $0F ;;addition + DEFB $A1 ;;stk-one + DEFB $03 ;;subtract + + DEFB $86 ;;series-06 + DEFB $14 ;;Exponent: $64, Bytes: 1 + DEFB $E6 ;;(+00,+00,+00) + DEFB $5C ;;Exponent: $6C, Bytes: 2 + DEFB $1F,$0B ;;(+00,+00) + DEFB $A3 ;;Exponent: $73, Bytes: 3 + DEFB $8F,$38,$EE ;;(+00) + DEFB $E9 ;;Exponent: $79, Bytes: 4 + DEFB $15,$63,$BB,$23 ;; + DEFB $EE ;;Exponent: $7E, Bytes: 4 + DEFB $92,$0D,$CD,$ED ;; + DEFB $F1 ;;Exponent: $81, Bytes: 4 + DEFB $23,$5D,$1B,$EA ;; + DEFB $04 ;;multiply + DEFB $38 ;;end-calc + + RET ; return. + +;----------------------- +; THE 'TANGENT' FUNCTION +;----------------------- +; (offset $21: 'tan') +; +; Evaluates tangent x as sin(x) / cos(x). +; +; +; /| +; h / | +; / |o +; /x | +; /----| +; a +; +; the tangent of angle x is the ratio of the length of the opposite side +; divided by the length of the adjacent side. As the opposite length can +; be calculates using sin(x) and the adjacent length using cos(x) then +; the tangent can be defined in terms of the previous two functions. + +; Error 6 if the argument, in radians, is too close to one like pi/2 +; which has an infinite tangent. e.g. PRINT TAN (PI/2) evaluates as 1/0. +; Similarly PRINT TAN (3*PI/2), TAN (5*PI/2) etc. + +;; tan +L37DA: RST 28H ;; FP-CALC x. + DEFB $31 ;;duplicate x, x. + DEFB $1F ;;sin x, sin x. + DEFB $01 ;;exchange sin x, x. + DEFB $20 ;;cos sin x, cos x. + DEFB $05 ;;division sin x/cos x (= tan x). + DEFB $38 ;;end-calc tan x. + + RET ; return. + +;---------------------- +; THE 'ARCTAN' FUNCTION +;---------------------- +; (Offset $24: 'atn') +; the inverse tangent function with the result in radians. +; This is a fundamental transcendental function from which others such as asn +; and acs are directly, or indirectly, derived. +; It uses the series generator to produce Chebyshev polynomials. + +;; atn +L37E2: CALL L3297 ; routine re-stack + LD A,(HL) ; fetch exponent byte. + CP $81 ; compare to that for 'one' + JR C,L37F8 ; forward, if less, to SMALL + + RST 28H ;; FP-CALC + DEFB $A1 ;;stk-one + DEFB $1B ;;negate + DEFB $01 ;;exchange + DEFB $05 ;;division + DEFB $31 ;;duplicate + DEFB $36 ;;less-0 + DEFB $A3 ;;stk-pi/2 + DEFB $01 ;;exchange + DEFB $00 ;;jump-true + DEFB $06 ;;to L37FA, CASES + + DEFB $1B ;;negate + DEFB $33 ;;jump + DEFB $03 ;;to L37FA, CASES + +;; SMALL +L37F8: RST 28H ;; FP-CALC + DEFB $A0 ;;stk-zero + +;; CASES +L37FA: DEFB $01 ;;exchange + DEFB $31 ;;duplicate + DEFB $31 ;;duplicate + DEFB $04 ;;multiply + DEFB $31 ;;duplicate + DEFB $0F ;;addition + DEFB $A1 ;;stk-one + DEFB $03 ;;subtract + DEFB $8C ;;series-0C + DEFB $10 ;;Exponent: $60, Bytes: 1 + DEFB $B2 ;;(+00,+00,+00) + DEFB $13 ;;Exponent: $63, Bytes: 1 + DEFB $0E ;;(+00,+00,+00) + DEFB $55 ;;Exponent: $65, Bytes: 2 + DEFB $E4,$8D ;;(+00,+00) + DEFB $58 ;;Exponent: $68, Bytes: 2 + DEFB $39,$BC ;;(+00,+00) + DEFB $5B ;;Exponent: $6B, Bytes: 2 + DEFB $98,$FD ;;(+00,+00) + DEFB $9E ;;Exponent: $6E, Bytes: 3 + DEFB $00,$36,$75 ;;(+00) + DEFB $A0 ;;Exponent: $70, Bytes: 3 + DEFB $DB,$E8,$B4 ;;(+00) + DEFB $63 ;;Exponent: $73, Bytes: 2 + DEFB $42,$C4 ;;(+00,+00) + DEFB $E6 ;;Exponent: $76, Bytes: 4 + DEFB $B5,$09,$36,$BE ;; + DEFB $E9 ;;Exponent: $79, Bytes: 4 + DEFB $36,$73,$1B,$5D ;; + DEFB $EC ;;Exponent: $7C, Bytes: 4 + DEFB $D8,$DE,$63,$BE ;; + DEFB $F0 ;;Exponent: $80, Bytes: 4 + DEFB $61,$A1,$B3,$0C ;; + DEFB $04 ;;multiply + DEFB $0F ;;addition + DEFB $38 ;;end-calc + + RET ; return. + + +;---------------------- +; THE 'ARCSIN' FUNCTION +;---------------------- +; (Offset $22: 'asn') +; the inverse sine function with result in radians. +; derived from arctan function above. +; Error A unless the argument is between -1 and +1 inclusive. +; uses an adaptation of the formula asn(x) = atn(x/sqr(1-x*x)) +; +; +; /| +; 1 / | +; / |x +; /a | +; /----| +; y +; +; e.g. we know the opposite side (x) and hypotenuse (1) +; and we wish to find angle a in radians. +; we can derive length y by Pythagorus and then use ATN instead. +; since y*y + x*x = 1*1 (Pythagorus Theorem) then +; y=sqr(1-x*x) - no need to multiply 1 by itself. +; so, asn(a) = atn(x/y) +; or more fully, +; asn(a) = atn(x/sqr(1-x*x)) + +; Close but no cigar. + +; While PRINT ATN (x/SQR (1-x*x)) gives the same results as PRINT ASN x, +; it leads to division by zero when x is 1 or -1. +; To overcome this, 1 is added to y giving half the required angle and the +; result is then doubled. +; That is PRINT ATN (x/(SQR (1-x*x) +1)) *2 +; A value higher than 1 gives the required error as attempting to find the +; square root of a negative number generates an error in Sinclair BASIC. + +;; asn +L3833: RST 28H ;; FP-CALC x. + DEFB $31 ;;duplicate x, x. + DEFB $31 ;;duplicate x, x, x. + DEFB $04 ;;multiply x, x*x. + DEFB $A1 ;;stk-one x, x*x, 1. + DEFB $03 ;;subtract x, x*x-1. + DEFB $1B ;;negate x, 1-x*x. + DEFB $28 ;;sqr x, sqr(1-x*x) = y + DEFB $A1 ;;stk-one x, y, 1. + DEFB $0F ;;addition x, y+1. + DEFB $05 ;;division x/y+1. + DEFB $24 ;;atn a/2 (half the angle) + DEFB $31 ;;duplicate a/2, a/2. + DEFB $0F ;;addition a. + DEFB $38 ;;end-calc a. + + RET ; return. + + +;------------------------- +; THE 'ARCCOS' FUNCTION +;------------------------- +; (Offset $23: 'acs') +; the inverse cosine function with the result in radians. +; Error A unless the argument is between -1 and +1. +; Result in range 0 to pi. +; Derived from asn above which is in turn derived from the preceding atn. +; It could have been derived directly from atn using acs(x) = atn(sqr(1-x*x)/x). +; However, as sine and cosine are horizontal translations of each other, +; uses acs(x) = pi/2 - asn(x) + +; e.g. the arccosine of a known x value will give the required angle b in +; radians. +; We know, from above, how to calculate the angle a using asn(x). +; Since the three angles of any triangle add up to 180 degrees, or pi radians, +; and the largest angle in this case is a right-angle (pi/2 radians), then +; we can calculate angle b as pi/2 (both angles) minus asn(x) (angle a). +; +; +; /| +; 1 /b| +; / |x +; /a | +; /----| +; y +; + +;; acs +L3843: RST 28H ;; FP-CALC x. + DEFB $22 ;;asn asn(x). + DEFB $A3 ;;stk-pi/2 asn(x), pi/2. + DEFB $03 ;;subtract asn(x) - pi/2. + DEFB $1B ;;negate pi/2 -asn(x) = acs(x). + DEFB $38 ;;end-calc acs(x). + + RET ; return. + + +; -------------------------- +; THE 'SQUARE ROOT' FUNCTION +; -------------------------- +; (Offset $28: 'sqr') +; This routine is remarkable only in its brevity - 7 bytes. +; It wasn't written here but in the ZX81 where the programmers had to squeeze +; a bulky operating sytem into an 8K ROM. It simply calculates +; the square root by stacking the value .5 and continuing into the 'to-power' +; routine. With more space available the much faster Newton-Raphson method +; should have been used as on the Jupiter Ace. + +;; sqr +L384A: RST 28H ;; FP-CALC + DEFB $31 ;;duplicate + DEFB $30 ;;not + DEFB $00 ;;jump-true + DEFB $1E ;;to L386C, LAST + + DEFB $A2 ;;stk-half + DEFB $38 ;;end-calc + + +; ------------------------------ +; THE 'EXPONENTIATION' OPERATION +; ------------------------------ +; (Offset $06: 'to-power') +; This raises the first number X to the power of the second number Y. +; As with the ZX80, +; 0 ^ 0 = 1. +; 0 ^ +n = 0. +; 0 ^ -n = arithmetic overflow. +; + +;; to-power +L3851: RST 28H ;; FP-CALC X, Y. + DEFB $01 ;;exchange Y, X. + DEFB $31 ;;duplicate Y, X, X. + DEFB $30 ;;not Y, X, (1/0). + DEFB $00 ;;jump-true + DEFB $07 ;;to L385D, XISO if X is zero. + +; else X is non-zero. Function 'ln' will catch a negative value of X. + + DEFB $25 ;;ln Y, LN X. + DEFB $04 ;;multiply Y * LN X. + DEFB $38 ;;end-calc + + JP L36C4 ; jump back to EXP routine -> + +; --- + +; these routines form the three simple results when the number is zero. +; begin by deleting the known zero to leave Y the power factor. + +;; XISO +L385D: DEFB $02 ;;delete Y. + DEFB $31 ;;duplicate Y, Y. + DEFB $30 ;;not Y, (1/0). + DEFB $00 ;;jump-true + DEFB $09 ;;to L386A, ONE if Y is zero. + + DEFB $A0 ;;stk-zero Y, 0. + DEFB $01 ;;exchange 0, Y. + DEFB $37 ;;greater-0 0, (1/0). + DEFB $00 ;;jump-true 0. + DEFB $06 ;;to L386C, LAST if Y was any positive + ;; number. + +; else force division by zero thereby raising an Arithmetic overflow error. +; There are some one and two-byte alternatives but perhaps the most formal +; might have been to use end-calc; rst 08; defb 05. + + DEFB $A1 ;;stk-one 0, 1. + DEFB $01 ;;exchange 1, 0. + DEFB $05 ;;division 1/0 ouch! + +; --- + +;; ONE +L386A: DEFB $02 ;;delete . + DEFB $A1 ;;stk-one 1. + +;; LAST +L386C: DEFB $38 ;;end-calc last value is 1 or 0. + + RET ; return. Whew! + + +;********************************* +;** Spectrum 128 Patch Routines ** +;********************************* + +; The new code added to the standard 48K Spectrum ROM is mainly devoted to the scanning and decoding of the keypad. +; These routines occupy addresses 386E through to 3B3A. Addresses 3B3B through to 3C96 contain a variety of routines for the following purposes: displaying the new tokens 'PLAY' and 'SPECTRUM', +; dealing with the keypad when using INKEY$, handling new 128 BASIC error messages, and producing the TV tuner display. Addresses 3BE1 to 3BFE and addresses 3C97 to 3CFF are unused and all contain 00. +; Documented by Paul Farrow. + +; -------------------------------- +; SCAN THE KEYPAD AND THE KEYBOARD +; -------------------------------- +; This patch will attempt to scan the keypad if in 128K mode and will then scan the keyboard. + +;; KEYS +L386E: PUSH IX + BIT 4,(IY+$01) ; [FLAGS] Test if in 128K mode + JR Z,L3879 ; Z=in 48K mode + + CALL L3A42 ; Attempt to scan the keypad + +;; KEYS_CONT +L3879: CALL L02BF ; Scan the keyboard + POP IX + RET + +; ---------------------------------- +; READ THE STATE OF THE OUTPUT LINES +; ---------------------------------- +; This routine returns the state of the four output lines (bits 0-3) in the lower four bits of L. The LSB of L corresponds to the output communication line to the keypad. +; In this way the state of the other three outputs are maintained when the state of the LSB of L is changed and sent out to register 14 of the AY-3-8912. + +;; READ_OUTPUTS +L387F: LD C,$FD ; FFFD = Address of the + LD D,$FF ; command register (register 7) + LD E,$BF ; BFFD = Address of the + LD B,D ; data register (register 14) + LD A,$07 + OUT (C),A ; Select command register + IN H,(C) ; Read its status + LD A,$0E + OUT (C),A ; Select data register + IN A,(C) ; Read its status + OR $F0 ; Mask off the input lines + LD L,A ; L=state of output lines at the + RET ; keypad socket + +; -------------------------- +; SET THE OUTPUT LINE, BIT 0 +; -------------------------- +; The output line to the keypad is set via the LSB of L. + +;; SET_REG14 +L3896: LD B,D + LD A,$0E + OUT (C),A ; Select the data register + LD B,E + OUT (C),L ; Send L out to the data register + RET ; Set the output line + +; ---------------------------------------- +; FETCH THE STATE OF THE INPUT LINE, BIT 5 +; ---------------------------------------- +; Return the state of the input line from the keypad in bit 5 of A. + +;; GET_REG14 +L389F: LD B,D + LD A,$0E + OUT (C),A ; Select the data register + IN A,(C) ; Read the input line + RET + +; ------------------------------ +; SET THE OUTPUT LINE LOW, BIT 0 +; ------------------------------ + +;; RESET_LINE +L38A7: LD A,L + AND $FE ; Reset bit 0 of L + LD L,A + JR L3896 ; Send out L to the data register + +; ------------------------------- +; SET THE OUTPUT LINE HIGH, BIT 0 +; ------------------------------- + +;; SET_LINE +L38AD: LD A,L + OR $01 ; Set bit 0 of L + LD L,A + JR L3896 ; Send out L to the data register + +; ------------------- +; MINOR DELAY ROUTINE +; ------------------- +; Delay for (B*13)+5 T-States. + +;; DELAY +L38B3: DJNZ L38B3 + RET + +; ------------------- +; MAJOR DELAY ROUTINE +; ------------------- +; Delay for (B*271)+5 T-states. + +;; DELAY2 +L38B6: PUSH BC + LD B,$10 + CALL L38B3 ; Inner delay of 135 T-States + POP BC + DJNZ L38B6 + + RET + +; ------------------------------------ +; MONITOR FOR THE INPUT LINE TO GO LOW +; ------------------------------------ +; Monitor the input line, bit 5, for up to (B*108)+5 T-states. + +;; MON_B5_LO +L38C0: PUSH BC + CALL L389F ; Read the state of the input line + POP BC + AND $20 ; Test bit 5, the input line + JR Z,L38CB ; Exit if input line found low + DJNZ L38C0 ; Repeat until timeout expires + +;; EXT_MON_LO +L38CB: RET + +; ------------------------------------- +; MONITOR FOR THE INPUT LINE TO GO HIGH +; ------------------------------------- +; Monitor the input line, bit 5, for up to (B*108)+5 T-states. + +;; MON_B5_HI +L38CC: PUSH BC + CALL L389F ; Read the state of the input line + POP BC + AND $20 ; Test bit 5, the input line + JR NZ,L38D7 ; Exit if input line found low + DJNZ L38CC ; Repeat until timeout expires + +;; EXT_MON_HI +L38D7: RET + +; ------------------------- +; READ KEY PRESS STATUS BIT +; ------------------------- +; This entry point is used to read in the status bit for a keypad row. If a key is being pressed in the current row then the bit read in will be a 1. + +;; READ_STATUS +L38D8: CALL L387F ; Read the output lines + LD B,$01 ; Read in one bit + JR L38E4 + +; ---------------- +; READ IN A NIBBLE +; ---------------- +; This entry point is used to read in a nibble of data from the keypad. It is used for two functions. The first is to read in the poll nibble and the second is to read in a row of key press data. +; For a nibble of key press data, a bit read in as 1 indicates that the corresponding key was pressed. + +;; READ_NIBBLE +L38DF: CALL L387F ; Read the state of the output lines + LD B,$04 ; Read in four bits + +;; READ_BIT +L38E4: PUSH BC + CALL L389F ; Read the input line from the keypad + POP BC + AND $20 ; This line should initially be high + JR Z,L392D ; Z=read in a 0, there must be an error + + XOR A ; The bits read in will be stored in register A + +;; BIT_LOOP +L38EE: PUSH BC ; Preserve the loop count and any bits + PUSH AF ; read in so far + CALL L38AD ; Set the output line high + + LD B,$A3 ; Monitor for 17609 T-states for the + CALL L38C0 ; input line to go low + JR NZ,L392B ; NZ=the line did not go low + + CALL L38A7 ; Set the output line low + JR L3901 ; Insert a delay of 12 T-states + +L38FF: DEFB $FF, $FF + +;; BL_CONTINUE +L3901: LD B,$2B ; Delay for 564 T-states + CALL L38B3 + CALL L389F ; Read in the bit value + BIT 5,A + JR Z,L3911 ; Z=read in a 0 + + POP AF ; Retrieve read in bits + SCF ; Set carry bit + JR L3914 + +;; BL_READ_0 +L3911: POP AF ; Retrieve read in bits + SCF + CCF ; Clear carry bit + +;; BL_STORE +L3914: RRA ; Shift the carry bit into bit 0 of A + PUSH AF ; Save bits read in + CALL L38AD ; Set the output line high + + LD B,$26 ; Delay for 499 T-states + CALL L38B3 + + CALL L38A7 ; Set the output line low + + LD B,$23 ; Delay for 460 T-states + CALL L38B3 + + POP AF ; Retrieve read in bits + POP BC ; Retrieve loop counter and repeat + DJNZ L38EE ; for all bits to read in + + RET + +; ---------- +; LINE ERROR +; ---------- +; The input line was found at the wrong level. The output line is now set high which will eventually cause the keypad to abandon its transmissions. +; The upper nibble of system variable FLAGS/ROW3 will be cleared to indicate that communications to the keypad is no longer in progress. + +;; LINE_ERROR +L392B: POP AF + POP BC ; Clear the stack + +;; LINE_ERROR2 +L392D: CALL L38AD ; Set the output line high + + XOR A ; Clear FLAGS nibble + LD ($5B88),A ; [FLAGS/ROW3] + + INC A ; Return zero flag reset + SCF + CCF ; Return carry flag reset + RET + +; --------------- +; POLL THE KEYPAD +; --------------- +; The Spectrum 128 polls the keypad by changing the state of the output line and monitoring for responses from the keypad on the input line. +; Before a poll occurs, the poll counter must be decremented until it reaches zero. This counter causes a delay of three seconds before a communications attempt to the keypad is made. +; The routine can exit at five different places and it is the state of the A register, the zero flag and the carry flag which indicates the cause of the exit. This is summarised below: +; +; A Register Zero Flag Carry Flag Cause +; 0 set set Communications already established +; 0 set reset Nibble read in OK +; 1 reset reset Nibble read in with an error or i/p line initially low +; 1 reset set Poll counter has not yet reached zero +; +; The third bit of the nibble read in must be set for the poll to be subsequently accepted. + +;; ATTEMPT_POLL +L3938: CALL L387F ; Read the output line states + + LD A,($5B88) ; [FLAGS/ROW3] Has communications already been + AND $80 ; established with the keypad? + JR NZ,L3999 ; NZ=yes, so skip the poll + + CALL L389F ; Read the input line + AND $20 ; It should be high initially + JR Z,L392D ; Z=error, input line found low + + LD A,($5B88) ; [FLAGS/ROW3] Test if poll counter already zero thus + AND A ; indicating a previous comms error + JR NZ,L395A ; NZ=ready to poll the keypad + + INC A ; Indicate comms not established + LD ($5B88),A ; [FLAGS/ROW3] + LD A,$4C ; Reset the poll counter + LD ($5B89),A ; [ROW2/ROW1] + JR L399C ; Exit the routine + +;;POLL_KEYPAD +L395A: LD A,($5B89) ; [ROW2/ROW1] Decrement the poll counter + DEC A + LD ($5B89),A ; [ROW2/ROW1] + JR NZ,L399C ; Exit the routine if it is not yet zero + +; The poll counter has reached zero so a poll of the keypad can now occur. + + XOR A + LD ($5B88),A ; [FLAGS/ROW3] Indicate that a poll can occur + LD ($5B89),A ; [ROW2/ROW1] + LD ($5B8A),A ; [ROW4/ROW5] Clear all the row nibble stores + + CALL L38A7 ; Set the output line low + + LD B,$21 ; Wait up to 3569 T-States for the + CALL L38C0 ; input line to go low + JR NZ,L392D ; NZ=line did not go low + + CALL L38AD ; Set the output line high + + LD B,$24 ; Wait up to 3893 T-States for the + CALL L38CC ; input line to go high + JR Z,L392D ; NZ=line did not go high + + CALL L38A7 ; Set the output line low + + LD B,$0F + CALL L38B6 ; Delay for 4070 T-States + CALL L38DF ; Read in a nibble of data + JR NZ,L392D ; NZ=error occurred when reading in nibble + + SET 7,A ; Set bit 7 + AND $F0 ; Keep only the upper four bits + ; (Bit 6 will be set if poll successful) + LD ($5B88),A ; [FLAGS/ROW3] Store the flags nibble + XOR A + SRL A ; Exit: Zero flag set, Carry flag reset + RET + +;; AP_SKIP_POLL +L3999: XOR A ; Communications already established + SCF ; Exit: Zero flag set, Carry flag set + RET + +;; PK_EXIT +L399C: XOR A ; Poll counter not zero + INC A + SCF ; Exit: Zero flag reset, Carry flag set + RET + +; ----------------------- +; SCAN THE KEYPAD ROUTINE +; ----------------------- +; If a successful poll of the keypad occurs then the five rows of keys are read in and a unique key code generated. + +;; KEYPAD_SCAN +L39A0: CALL L3938 ; Try to poll the keypad + + LD A,($5B88) ; [FLAGS/ROW3] Test the flags nibble + CPL + AND $C0 ; Bits 6 and 7 must be set in FLAGS + RET NZ ; NZ=poll was not successful + +; The poll was successful so now read in data for the five keypad rows. + + LD IX,$5B8A ; [ROW4/ROW5] + LD B,$05 ; The five rows + +;; KS_LOOP +L39B0: PUSH BC ; Save counter + + CALL L38D8 ; Read the key press status bit + JP NZ,L3A3A ; NZ=error occurred + + BIT 7,A ; Test the bit read in + JR Z,L39DC ; Z=no key pressed in this row + + CALL L38DF ; Read in the row's nibble of data + JR NZ,L3A3A ; NZ=error occurred + + POP BC ; Fetch the nibble loop counter + PUSH BC + LD C,A ; Move the nibble read in to C + LD A,(IX+$00) ; Fetch the nibble store + BIT 0,B ; Test if an upper or lower nibble + JR Z,L39D6 ; Z=upper nibble + + SRL C ; Shift the nibble to the lower position + SRL C + SRL C + SRL C + AND $F0 ; Mask off the lower nibble of the + JR L39D8 ; nibble store + +;; KS_UPPER +L39D6: AND $0F ; Mask off the upper nibble of the nibble store + +;; KS_STORE +L39D8: OR C ; Combine the existing and new + LD (IX+$00),A ; nibbles and store them + +;; KS_NEXT +L39DC: POP BC ; Retrieve the row counter + BIT 0,B ; Test if next nibble store is required + JR NZ,L39E3 ; NZ=use same nibble store + + DEC IX ; Point to the next nibble store + +;; KS_NEW +L39E3: DJNZ L39B0 ; Repeat for the next keypad row + +; All five rows have now been read so compose a unique code for the key pressed. + + LD E,$80 ; Signal no key press found yet + LD IX,$5B88 ; [FLAGS/ROW3] + LD HL,$3A3F ; Point to the key mask data + LD B,$03 ; Scan three nibbles + +;; GEN_LOOP +L39F0: LD A,(IX+$00) ; Fetch a pair of nibbles + AND (HL) ; This will mask off the FLAGS nibble and the SHIFT/0 key + + JR Z,L3A17 ; Z=no key pressed in these nibbles + + BIT 7,E ; Test if a key has already been found + JR Z,L3A3C ; Z=multiple keys pressed + + PUSH BC ; Save the loop counter + PUSH AF ; Save the byte of key bit data + LD A,B ; Move loop counter to A + JR L3A01 ; A delay of 12 T-States + +L39FF: DEFB $FF, $FF ; Unused locations + +;; GEN_CONT +L3A01: DEC A ; These lines of code generate base + SLA A ; values of 7, 15 and 23 for the three + SLA A ; nibble stores 5B88, 5B89 & 5B8A. + SLA A + OR $07 + LD B,A ; B=(loop counter-1)*8+7 + POP AF ; Fetch the byte of key press data + +;; GEN_BIT +L3A0C: SLA A ; Shift until a set key bit drops into the + JP C,L3A13 ; carry flag + + DJNZ L3A0C ; Decrement B for each 'unsuccessful' shift of the A register + +;; GEN_FOUND +L3A13: LD E,B ; E=a unique number for the key pressed, between 1 - 19 except 2 & 3 + + POP BC ; As a result shifting the set key bit + ; into the carry flag, the A register will + ; hold 00 if only one key was pressed + JR NZ,L3A3C ; NZ=multiple keys pressed + +;; GEN_NEXT +L3A17: INC IX ; Point to the next nibble store + INC HL ; Point to the corresponding mask data + DJNZ L39F0 ; Repeat for all three 'nibble' bytes + + BIT 7,E ; Test if any keys were pressed + JR NZ,L3A27 ; NZ=no keys were pressed + + LD A,E ; Copy the key code + AND $FC ; Test for the '.' key (E=1) + JR Z,L3A27 ; Z='.' key pressed + + DEC E + DEC E ; Key code in range 2 - 17 + +; The E register now holds a unique key code value between 1 and 17. + +;; GEN_POINT +L3A27: LD A,($5B8A) ; [ROW4/ROW5] Test if the SHIFT key was pressed + AND $08 + JR Z,L3A34 ; Z=the SHIFT key was not pressed + +; The SHIFT key was pressed or no key was pressed. + + LD A,E ; Fetch the key code + AND $7F ; Mask off 'no key pressed' bit + ADD A,$12 ; Add on a shift offset of 12 + LD E,A + +; Add a base offset of 5A to all key codes. Note that no key press will result in a key code of DA. This is the only code with bit 7 set and so will be detected later. + +;; GEN_NOSHIFT +L3A34: LD A,E + ADD A,$5A ; Add a base offset of 5A + LD E,A ; Return key codes in range 5B - 7D + XOR A + RET ; Exit: Zero flag set, key found OK + +; These two lines belong with the loop above to read in the five keypad rows and are jumped to when an error occurs during reading in a nibble of data. + +;; KS_ERROR +L3A3A: POP BC ; Clear the stack and exit + RET ; Exit: Zero flag reset + +;; GEN_INVALID +L3A3C: XOR A ; Exit: Zero flag reset indicating an + INC A ; invalid key press + RET + +; ---------------- +; KEYPAD MASK DATA +; ---------------- + +;; KEY_MASKS +L3A3F: DEFB $0F, $FF, $F2 ; Key mask data + +; --------------- +; READ THE KEYPAD +; --------------- +; This routine reads the keypad and handles key repeat and decoding. The bulk of the key repeat code is very similar to that used in the equivalent keyboard routine and works are follows. +; A double system of KSTATE system variables (KSTATE0 - KSTATE3 and KSTATE4 - KSTATE7) is used to allow the detection of one key while in the repeat period of the previous key. +; In this way, a 'spike' from another key will not stop the previous key from repeating. For a new key to be acknowledged, it must be held down for at least 1/5th of a second, i.e. ten calls to KEYPAD. +; The KSTATE system variables store the following data: +; +; KSTATE0/4 Un-decoded Key Value (00-27 for keyboard, 5B-7D for keypad, FF for no key) +; KSTATE1/5 10 Call Counter +; KSTATE2/6 Repeat Delay +; KSTATE3/7 Decoded Key Value +; +; The code returned is then stored in system variable LAST_K (5C08) and a new key signalled by setting bit 5 of FLAGS (5C3B). +; +; If the Spectrum 128 were to operate identically to the standard 48K Spectrum when in 48K mode, it would have to spend zero time in reading the keypad. +; As this is not possible, the loading on the CPU is reduced by scanning the keypad upon every other interrupt. A '10 Call Counter' is then used to ensure that a key is held down for at least 1/5th of a second +; before it is registered. Note that this is twice as long as for keyboard key presses and so the keypad key repeat delay is halved. +; +; At every other interrupt the keypad scanning routine is skipped. The net result of the routine is simply to decrement both '10 Call Counters', if appropriate. By loading the E register with 80 ensures that +; the call to KP_TEST will reject the key code and cause a return. A test for keyboard key codes prevents the Call Counter decrements affecting a keyboard key press. It would have been more efficient to execute +; a return upon every other call to KEYPAD and then to have used a '5 Call Counter' just as the keyboard routine does. +; +; A side effect of both the keyboard and keypad using the same KSTATE system variables is that if a key is held down on the keypad and then a key is held down on the keyboard, both keys will be monitored and +; repeated alternatively, but with a reduced repeat delay. This delay is between the keypad key repeat delay and the keyboard key repeat delay. This occurs because both the keypad and keyboard routines will +; decrement the KSTATE system variable Call Counters. The keypad routine 'knows' of the existence of keyboard key codes but the reverse is not true. + +;; KEYPAD +L3A42: LD E,$80 ; Signal no key pressed + LD A,($5C78) ; [FRAMES] + AND $01 ; Scan the keypad every other + JR NZ,L3A4F ; interrupt + + CALL L39A0 + RET NZ ; NZ=no valid key pressed + +;; KP_CHECK +L3A4F: LD HL,$5C00 ; [KSTATE0] Test the first KSTATE variable + +;; KP_LOOP +L3A52: BIT 7,(HL) ; Is the set free? + JR NZ,L3A62 ; NZ=yes + + LD A,(HL) ; Fetch the un-decoded key value + CP $5B ; Is it a keyboard code? + JR C,L3A62 ; C=yes, so do not decrement counter + + INC HL + DEC (HL) ; Decrement the 10 Call Counter + DEC HL + JR NZ,L3A62 ; If the counter reaches zero, then + ; signal the set is free + LD (HL),$FF + +;; KP_CH_SET +L3A62: LD A,L ; Jump back and test the second set if + LD HL,$5C04 ; [KSTATE4] not yet considered + CP L + JR NZ,L3A52 + + CALL L3AAE ; Test for valid key combinations and + RET NZ ; return if invalid + + LD A,E ; Test if the key in the first set is being + LD HL,$5C00 ; [KSTATE0] repeated + CP (HL) + JR Z,L3A9E ; Jump if being repeated + + EX DE,HL ; Save the address of KSTATE0 + LD HL,$5C04 ; [KSTATE4] Test if the key in the second set is + CP (HL) ; being repeated + JR Z,L3A9E ; Jump if being repeated + +; A new key will not be accepted unless one of the KSTATE sets is free. + + BIT 7,(HL) ; Test if the second set is free + JR NZ,L3A83 ; Jump if set is free + + EX DE,HL + BIT 7,(HL) ; Test if the first set is free + RET Z ; Return if no set is free + +;; KP_NEW +L3A83: LD E,A ; Pass the key code to the E register + LD (HL),A ; and to KSTATE0/4 + INC HL + LD (HL),$0A ; Set the '10 Call Counter' to 10 + INC HL + + LD A,($5C09) ; [REPDEL] Fetch the initial repeat delay + SRL A ; Divide delay by two + LD (HL),A ; Store the repeat delay + INC HL + + CALL L3AD7 ; Decode the keypad key code + LD (HL),E ; and store it in KSTATE3/7 + +; This section is common for both new keys and repeated keys. + +;; KP_END +L3A94: LD A,E + LD ($5C08),A ; [LAST_K] Store the key value in LAST_K + LD HL,$5C3B ; FLAGS + SET 5,(HL) ; Signal a new key pressed + RET + +; ------------------------- +; THE KEY REPEAT SUBROUTINE +; ------------------------- + +;; KP_REPEAT +L3A9E: INC HL + LD (HL),$0A ; Reset the '10 Call Counter' to 10 + INC HL + DEC (HL) ; Decrement the repeat delay + RET NZ ; Return if not zero + + LD A,($5C0A) ; [REPPER] The subsequent repeat delay is + SRL A ; divided by two and stored + LD (HL),A + INC HL + LD E,(HL) ; The key repeating is fetched + JR L3A94 ; and then returned in LAST_K + +; ---------------------------------------- +; THE TEST FOR A VALID KEY CODE SUBROUTINE +; ---------------------------------------- +; The zero flag is returned set if the key code is valid. No key press, SHIFT only or invalid shifted key presses return the zero flag reset. + +;; KP_TEST +L3AAE: LD A,E + LD HL,$5B66 ; FLAGS3 Test if in BASIC or EDIT mode + BIT 0,(HL) + JR Z,L3ABC ; Z=EDIT mode + +; Test key codes when in BASIC/CALCULATOR mode + + CP $6D ; Test for shifted keys + JR NC,L3AD4 ; and signal an error if found + +;; KPT_OK +L3ABA: XOR A ; Signal valid key code + RET ; Exit: Zero flag set + +; Test key codes when in EDIT/MENU mode. + +;; KPT_EDIT +L3ABC: CP $80 ; Test for no key press + JR NC,L3AD4 ; NC=no key press + + CP $6C ; Test for SHIFT on its own + JR NZ,L3ABA ; NZ=valid key code + +L3AC4: DEFB $00, $00, $00 ; Delay for 64 T-States + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00 + +;; KPT_INVALID +L3AD4: XOR A ; Signal invalid key code + INC A + RET ; Exit: Zero flag reset + +; --------------------------- +; THE KEY DECODING SUBROUTINE +; --------------------------- + +;; KP_DECODE +L3AD7: PUSH HL ; Save the KSTATE pointer + LD A,E + SUB $5B ; Reduce the key code range to + LD D,$00 ; 00 - 22 and transfer to DE + LD E,A + + LD HL,$5B66 ; FLAGS3 Test if in EDIT or BASIC mode + BIT 0,(HL) + JR Z,L3AEA ; Z=EDIT/MENU mode + +; Use Table 1 when in CALCULATOR/BASIC mode. + + LD HL,L3B13 + JR L3B0F ; Look up the key value + +; Deal with EDIT/MENU mode. + +;; KPD_EDIT +L3AEA: LD HL,L3B25 ; Use Table 4 for unshifted key + CP $11 ; presses + JR C,L3B0F + +; Deal with shifted keys in EDIT/MENU mode. + +; Use Table 3 with SHIFT 1 (delete to beginning of line), SHIFT 2 (delete to end of line), SHIFT 3 (SHIFT TOGGLE). Note that although SHIFT TOGGLE produces a unique valid code, +; it actually performs no function when editing a BASIC program. + + LD HL,L3B21 + CP $15 ; Test for SHIFT 1 + JR Z,L3B0F + + CP $16 ; Test for SHIFT 2 + JR Z,L3B0F + + JR L3B01 ; Delay for 12 T-States + +L3AFE: DEFB $00, $FF, $FF ; Unused locations + +;; KPD_CONT +L3B01: CP $17 ; Test for SHIFT 3 + JR Z,L3B0F + +; Use Table 2 with SHIFT 4 (delete to beginning of word) and SHIFT 5 (delete to end of word). + + LD HL,L3B18 + CP $21 ; Test for SHIFT 4 and above + JR NC,L3B0F + +;Use Table 1 for all other shifted key presses. + + LD HL,L3B13 + +;; KPD_EXIT +L3B0F: ADD HL,DE ; Look up the key value + LD E,(HL) + POP HL ; Retrieve the KSTATE address + RET + +; -------------------------------- +; THE KEYPAD DECODE LOOK-UP TABLES +; -------------------------------- + +;; KPD_TABLE1 +L3B13: DEFB $2E, $0D, $33 ; '.', ENTER, 3 + DEFB $32, $31 ; 2, 1 + +;; KPD_TABLE2 +L3B18: DEFB $29, $28, $2A ; ), (, * + DEFB $2F, $2D, $39 ; /, - , 9 + DEFB $38, $37, $2B ; 8, 7, + + +;; KPD_TABLE3 +L3B21: DEFB $36, $35, $34 ; 6, 5, 4 + DEFB $30 ; 0 + +;; KPD_TABLE4 +L3B25: DEFB $A5, $0D, $A6 ; Bottom, ENTER, Top + DEFB $A7, $A8, $A9 ; End of line, Start of line, TOGGLE + DEFB $AA, $0B, $0C ; DEL right, Up, DEL + DEFB $07, $09, $0A ; CMND, Right, Down + DEFB $08, $AC, $AD ; Left, Down ten, Up ten + DEFB $AE, $AF ; End word, Beginning of word + DEFB $B0, $B1, $B2 ; DEL to end of line, DEL to start of line, SHIFT TOGGLE + DEFB $B3, $B4 ; DEL to end of word, DEL to beginning of word + +; ----------------------------- +; PRINT NEW ERROR MESSAGE PATCH +; ----------------------------- + +L3B3B: BIT 4,(IY+$01) ; FLAGS 3 - In 128K mode? + JR NZ,L3B46 ; NZ=128K mode + +; In 48K mode + + XOR A ; Replicate code from standard ROM that the patch over-wrote + LD DE,$1536 + RET + +; In 128K mode + +L3B46: LD HL,$010F ; Vector table entry in Editor ROM -> JP $03A2 + +; Return to Editor ROM at address in HL + +L3B49: EX (SP),HL ; Change the return address + JP $5B00 ; Page Editor ROM and return to the address on the stack + +; ------------------------------------- +; STATEMENT INTERPRETATION RETURN PATCH +; ------------------------------------- + +L3B4D: BIT 4,(IY+$01) ; In 128K mode? + JR NZ,L3B58 ; NZ=128K mode + +; In 48K mode + + BIT 7,(IY+$0A) ; replicate code from standard ROM that the patch over-wrote + RET + +; In 128K mode + +L3B58: LD HL,$0112 ; Handle in Editor ROM by jumping to Vector table entry in Editor ROM -> JP #182A + JR L3B49 + +; -------------------------- +; GO TO NEXT STATEMENT PATCH +; -------------------------- + +L3B5D: BIT 4,(IY+$01) ; In 128K mode? + JR NZ,L3B67 ; NZ=128K mode + +; In 48K mode + + RST 18H ; replicate code from standard ROM that the patch over-wrote + CP $0D + RET + +; In 128K mode + +L3B67: LD HL,$0115 ; Handle in Editor ROM by jumping to Vector table entry in Editor ROM -> JP #18A8 + JR L3B49 + +; -------------------------------------- +; INKEY$ ROUTINE TO DEAL WITH THE KEYPAD +; -------------------------------------- + +;; KEYSCAN2 +L3B6C: CALL L028E ; KEYSCAN Scan the keyboard + LD C,$00 + JR NZ,L3B80 ; NZ=multiple keys + + CALL L031E ; K_TEST + JR NC,L3B80 ; NC=shift only or no key + + DEC D + LD E,A + CALL L0333 ; K_DECODE + JP L2657 ; S_CONT Get string and continue scanning + +;; KPI_SCAN +L3B80: BIT 4,(IY+$01) ; 128K mode? + JP Z,L2660 ; S_IK$_STK Z=no, stack keyboard code + + DI ; Disable interrupts whilst scanning + CALL L39A0 ; the keypad + EI + JR NZ,L3B9A ; NZ=multiple keys + + CALL L3AAE ; Test the keypad + JR NZ,L3B9A ; NZ=no key, shift only or invalid combination + + CALL L3AD7 ; Form the key code + LD A,E + JP L2657 ; S_CONT Get string and continue scanning + +;; KPI_INVALID +L3B9A: LD C,$00 ; Signal no key, i.e. length=0 + JP L2660 ; S_IK$_STK + +; --------------------- +; PRINT TOKEN/UDG PATCH +; --------------------- + +L3B9F: CP $A3 ; SPECTRUM (T) + JR Z,L3BAF + + CP $A4 ; PLAY (U) + JR Z,L3BAF + +; In 48K mode here + +L3BA7: SUB $A5 ; Check as per original ROM + JP NC,$0B5F + + JP $0B56 ; Rejoin original ROM routine + +L3BAF: BIT 4,(IY+$01) ; FLAGS3 - Bit 4=1 if in 128K mode + JR Z,L3BA7 ; Rejoin code for when in 48K mode + +; In 128K mode here + + LD DE,L3BC9 + PUSH DE ; Stack return address + + SUB $A3 ; Check whether the SPECTRUM token + + LD DE,L3BD2 ; SPECTRUM token + JR Z,L3BC3 + + LD DE,L3BDA ; PLAY token + +L3BC3: LD A,$04 ; Signal not RND, INKEY$ or PI so that a trailing space is printed + PUSH AF + JP L0C17 ; Rejoin printing routine PO-TABLE+3 + +; Return address from above + +L3BC9: SCF ; Return as if no trailing space + + BIT 1,(IY+$01) ; Test if printer is in use + RET NZ ; NZ=printer in use + + JP $0B03 ; PO-FETCH - Return via Position Fetch routine + +L3BD2: DEFM "SPECTRU" ; SPECTRUM token + DEFB 'M'+$80 + +L3BDA: DEFM "PLA" ; PLAY token + DEFB 'Y'+$80 + +;; KP_SCAN2 +L3BDE: JP L3C01 ; This is not called from either ROM. It can be used to scan the keypad. + +L3BE1: DEFB $00, $00, $00 ; Unused locations + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $00, $00, $00 + DEFB $FF, $FF + +;; KP_SCAN +L3C01: JP L39A0 ; This was to be called via the vector table in the EDITOR ROM but due to a programming error it never gets called. + +; ----------------------- +; TV TUNER VECTOR ENTRIES +; ----------------------- + +L3C04: JP L3C10 +L3C07: JP L3C10 +L3C0A: JP L3C10 +L3C0D: JP L3C10 + +; ---------------- +; TV TUNER ROUTINE +; ---------------- +; This routine generates a display showing all possible colours and emitting a continuous cycle of a 440 Hz tone for 1 second followed by silence for 1 second. +; Its purpose is to ease the tuning in of TV sets to the Spectrum 128's RF signal. The display consists of vertical stripes of width four character squares showing each of the eight colours +; available at both their normal and bright intensities. The display begins with white on the left progressing up to black on the right. With in each colour stripe in the first eight rows is +; shown the year '1986' in varying ink colours. This leads to a display that shows all possible ink colours on all possible paper colours. + +;; TV_TUNER +L3C10: LD A,$7F ; Test for the BREAK key + IN A,($FE) + RRA + RET C ; C=SPACE not pressed + + LD A,$FE + IN A,($FE) + RRA + RET C ; C=SPACE not pressed + + LD A,$07 + OUT ($FE),A ; Set the border to white + + LD A,$02 ; Open channel 2 (main screen) + CALL $1601 + + XOR A + LD ($5C3C),A ; [TV_FLAG] Signal using main screen + + LD A,$16 ; Print character 'AT' + RST 10H + + XOR A ; Print character '0' + RST 10H + + XOR A ; Print character '0' + RST 10H + + LD E,$08 ; Number of characters per colour + LD B,E ; Paper counter + 1 + LD D,B ; Ink counter + 1 + +;; TVT_ROW +L3C34: LD A,B ; Calculate the paper colour + DEC A ; Bits 3-5 of each screen attribute + RL A ; holds the paper colour; bits 0-2 + RL A ; the ink colour + RL A + ADD A,D ; Add the ink colour + DEC A + LD ($5C8F),A ; [ATTR_T] Store as temporary attribute value + + LD HL,L3C8F ; TVT_DATA Point to the 'year' data + LD C,E ; Get number of characters to print + +;; TVT_YEAR +L3C45: LD A,(HL) ; Fetch a character from the data + RST 10H ; Print it + INC HL + DEC C + JR NZ,L3C45 ; Repeat for the 8 characters + + DJNZ L3C34 ; Repeat for all colours in this row + + LD B,E ; Reset paper colour + DEC D ; Next ink colour + JR NZ,L3C34 ; Produce next row with new ink colour + + LD HL,$4800 ; Point to 2nd third of display file + LD D,H + LD E,L + INC DE ; Point to the next display cell + XOR A + LD (HL),A ; Clear first display cell + LD BC,$0FFF + LDIR ; Clear lower 2 thirds of display file + + EX DE,HL ; HL points to start of attributes file + LD DE,$5900 ; Point to 2nd third of attributes file + LD BC,$0200 + LDIR ; Copy screen attributes + +; Now that the display has been constructed, produce a continuous cycle of a 440 Hz tone for 1 second followed by a period of silence for 1 second (actually 962ms). + + DI ; Disable interrupts so that a pure tone can be generated + +;; TVT_TONE +L3C68: LD DE,$0370 ; DE=twice the tone frequency in Hz + LD L,$07 ; Border colour of white + +;; TVT_DURATION +L3C6D: LD BC,$0099 ; Delay for 950.4us + +;; TVT_PERIOD +L3C70: DEC BC + LD A,B + OR C + JR NZ,L3C70 + + LD A,L + XOR $10 ; Toggle the speaker output whilst + LD L,A ; preserving the border colour + OUT ($FE),A + + DEC DE ; Generate the tone for 1 second + LD A,D + OR E + JR NZ,L3C6D + +; At this point the speaker is turned off, so delay for 1 second. + + LD BC,$0000 ; Delay for 480.4us + +;; TVT_DELAY1 +L3C83: DEC BC + LD A,B + OR C + JR NZ,L3C83 + +;; TVT_DELAY2 +L3C88: DEC BC ; Delay for 480.4us + LD A,B + OR C + JR NZ,L3C88 + + JR L3C68 ; Repeat the tone cycle + +;; TVT_DATA +L3C8F: DEFB $13, $00 ; Bright, off + DEFB $31, $39 ; '1', '9' + DEFB $13, $01 ; Bright, on + DEFB $38, $36 ; '8', '6' + +; ------ +; UNUSED +; ------ + +L3C97: + BLOCK #3CF0-$,0 ;15600 +;> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >; +; ╔═══════════════════════════╗ +; ║ дополнения для SP48 !! ║ +; ║ для перехода EXP-BAS48 ║ +; ║ 16 байт + 22 байта ║ +; ╚═══════════════════════════╝ + + LD HL,#C0 ; POINT - вход в MENU из BASIC 48. for ZX_MODE.ASM - RET_FROM_BIOS_TO_BASIC48 + JR JMP_EX + + BLOCK #3CF8-$,0 +JMP_EX: PUSH HL + PUSH AF + LD A,ROM.BIOS ; переход в BIOS + OUT (CNF_PORT.ON),A + POP AF + RET ; возврат +;< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < <; + + BLOCK $3D00-$,00 ;15616 + +; ------------------------------- +; THE 'ZX SPECTRUM CHARACTER SET' +; ------------------------------- + +;; char-set + +; $20 - Character: ' ' CHR$(32) + +L3D00: DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + +; $21 - Character: '!' CHR$(33) + + DEFB %00000000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00000000 + DEFB %00010000 + DEFB %00000000 + +; $22 - Character: '"' CHR$(34) + + DEFB %00000000 + DEFB %00100100 + DEFB %00100100 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + +; $23 - Character: '#' CHR$(35) + + DEFB %00000000 + DEFB %00100100 + DEFB %01111110 + DEFB %00100100 + DEFB %00100100 + DEFB %01111110 + DEFB %00100100 + DEFB %00000000 + +; $24 - Character: '$' CHR$(36) + + DEFB %00000000 + DEFB %00001000 + DEFB %00111110 + DEFB %00101000 + DEFB %00111110 + DEFB %00001010 + DEFB %00111110 + DEFB %00001000 + +; $25 - Character: '%' CHR$(37) + + DEFB %00000000 + DEFB %01100010 + DEFB %01100100 + DEFB %00001000 + DEFB %00010000 + DEFB %00100110 + DEFB %01000110 + DEFB %00000000 + +; $26 - Character: '&' CHR$(38) + + DEFB %00000000 + DEFB %00010000 + DEFB %00101000 + DEFB %00010000 + DEFB %00101010 + DEFB %01000100 + DEFB %00111010 + DEFB %00000000 + +; $27 - Character: ''' CHR$(39) + + DEFB %00000000 + DEFB %00001000 + DEFB %00010000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + +; $28 - Character: '(' CHR$(40) + + DEFB %00000000 + DEFB %00000100 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00000100 + DEFB %00000000 + +; $29 - Character: ')' CHR$(41) + + DEFB %00000000 + DEFB %00100000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00100000 + DEFB %00000000 + +; $2A - Character: '*' CHR$(42) + + DEFB %00000000 + DEFB %00000000 + DEFB %00010100 + DEFB %00001000 + DEFB %00111110 + DEFB %00001000 + DEFB %00010100 + DEFB %00000000 + +; $2B - Character: '+' CHR$(43) + + DEFB %00000000 + DEFB %00000000 + DEFB %00001000 + DEFB %00001000 + DEFB %00111110 + DEFB %00001000 + DEFB %00001000 + DEFB %00000000 + +; $2C - Character: ',' CHR$(44) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00001000 + DEFB %00001000 + DEFB %00010000 + +; $2D - Character: '-' CHR$(45) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00111110 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + +; $2E - Character: '.' CHR$(46) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00011000 + DEFB %00011000 + DEFB %00000000 + +; $2F - Character: '/' CHR$(47) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000010 + DEFB %00000100 + DEFB %00001000 + DEFB %00010000 + DEFB %00100000 + DEFB %00000000 + +; $30 - Character: '0' CHR$(48) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000110 + DEFB %01001010 + DEFB %01010010 + DEFB %01100010 + DEFB %00111100 + DEFB %00000000 + +; $31 - Character: '1' CHR$(49) + + DEFB %00000000 + DEFB %00011000 + DEFB %00101000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00111110 + DEFB %00000000 + +; $32 - Character: '2' CHR$(50) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %00000010 + DEFB %00111100 + DEFB %01000000 + DEFB %01111110 + DEFB %00000000 + +; $33 - Character: '3' CHR$(51) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %00001100 + DEFB %00000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $34 - Character: '4' CHR$(52) + + DEFB %00000000 + DEFB %00001000 + DEFB %00011000 + DEFB %00101000 + DEFB %01001000 + DEFB %01111110 + DEFB %00001000 + DEFB %00000000 + +; $35 - Character: '5' CHR$(53) + + DEFB %00000000 + DEFB %01111110 + DEFB %01000000 + DEFB %01111100 + DEFB %00000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $36 - Character: '6' CHR$(54) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000000 + DEFB %01111100 + DEFB %01000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $37 - Character: '7' CHR$(55) + + DEFB %00000000 + DEFB %01111110 + DEFB %00000010 + DEFB %00000100 + DEFB %00001000 + DEFB %00010000 + DEFB %00010000 + DEFB %00000000 + +; $38 - Character: '8' CHR$(56) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %00111100 + DEFB %01000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $39 - Character: '9' CHR$(57) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %01000010 + DEFB %00111110 + DEFB %00000010 + DEFB %00111100 + DEFB %00000000 + +; $3A - Character: ':' CHR$(58) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00010000 + DEFB %00000000 + DEFB %00000000 + DEFB %00010000 + DEFB %00000000 + +; $3B - Character: ';' CHR$(59) + + DEFB %00000000 + DEFB %00000000 + DEFB %00010000 + DEFB %00000000 + DEFB %00000000 + DEFB %00010000 + DEFB %00010000 + DEFB %00100000 + +; $3C - Character: '<' CHR$(60) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000100 + DEFB %00001000 + DEFB %00010000 + DEFB %00001000 + DEFB %00000100 + DEFB %00000000 + +; $3D - Character: '=' CHR$(61) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00111110 + DEFB %00000000 + DEFB %00111110 + DEFB %00000000 + DEFB %00000000 + +; $3E - Character: '>' CHR$(62) + + DEFB %00000000 + DEFB %00000000 + DEFB %00010000 + DEFB %00001000 + DEFB %00000100 + DEFB %00001000 + DEFB %00010000 + DEFB %00000000 + +; $3F - Character: '?' CHR$(63) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %00000100 + DEFB %00001000 + DEFB %00000000 + DEFB %00001000 + DEFB %00000000 + +; $40 - Character: '@' CHR$(64) + + DEFB %00000000 + DEFB %00111100 + DEFB %01001010 + DEFB %01010110 + DEFB %01011110 + DEFB %01000000 + DEFB %00111100 + DEFB %00000000 + +; $41 - Character: 'A' CHR$(65) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %01000010 + DEFB %01111110 + DEFB %01000010 + DEFB %01000010 + DEFB %00000000 + +; $42 - Character: 'B' CHR$(66) + + DEFB %00000000 + DEFB %01111100 + DEFB %01000010 + DEFB %01111100 + DEFB %01000010 + DEFB %01000010 + DEFB %01111100 + DEFB %00000000 + +; $43 - Character: 'C' CHR$(67) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %01000000 + DEFB %01000000 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $44 - Character: 'D' CHR$(68) + + DEFB %00000000 + DEFB %01111000 + DEFB %01000100 + DEFB %01000010 + DEFB %01000010 + DEFB %01000100 + DEFB %01111000 + DEFB %00000000 + +; $45 - Character: 'E' CHR$(69) + + DEFB %00000000 + DEFB %01111110 + DEFB %01000000 + DEFB %01111100 + DEFB %01000000 + DEFB %01000000 + DEFB %01111110 + DEFB %00000000 + +; $46 - Character: 'F' CHR$(70) + + DEFB %00000000 + DEFB %01111110 + DEFB %01000000 + DEFB %01111100 + DEFB %01000000 + DEFB %01000000 + DEFB %01000000 + DEFB %00000000 + +; $47 - Character: 'G' CHR$(71) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %01000000 + DEFB %01001110 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $48 - Character: 'H' CHR$(72) + + DEFB %00000000 + DEFB %01000010 + DEFB %01000010 + DEFB %01111110 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %00000000 + +; $49 - Character: 'I' CHR$(73) + + DEFB %00000000 + DEFB %00111110 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00111110 + DEFB %00000000 + +; $4A - Character: 'J' CHR$(74) + + DEFB %00000000 + DEFB %00000010 + DEFB %00000010 + DEFB %00000010 + DEFB %01000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $4B - Character: 'K' CHR$(75) + + DEFB %00000000 + DEFB %01000100 + DEFB %01001000 + DEFB %01110000 + DEFB %01001000 + DEFB %01000100 + DEFB %01000010 + DEFB %00000000 + +; $4C - Character: 'L' CHR$(76) + + DEFB %00000000 + DEFB %01000000 + DEFB %01000000 + DEFB %01000000 + DEFB %01000000 + DEFB %01000000 + DEFB %01111110 + DEFB %00000000 + +; $4D - Character: 'M' CHR$(77) + + DEFB %00000000 + DEFB %01000010 + DEFB %01100110 + DEFB %01011010 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %00000000 + +; $4E - Character: 'N' CHR$(78) + + DEFB %00000000 + DEFB %01000010 + DEFB %01100010 + DEFB %01010010 + DEFB %01001010 + DEFB %01000110 + DEFB %01000010 + DEFB %00000000 + +; $4F - Character: 'O' CHR$(79) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $50 - Character: 'P' CHR$(80) + + DEFB %00000000 + DEFB %01111100 + DEFB %01000010 + DEFB %01000010 + DEFB %01111100 + DEFB %01000000 + DEFB %01000000 + DEFB %00000000 + +; $51 - Character: 'Q' CHR$(81) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000010 + DEFB %01000010 + DEFB %01010010 + DEFB %01001010 + DEFB %00111100 + DEFB %00000000 + +; $52 - Character: 'R' CHR$(82) + + DEFB %00000000 + DEFB %01111100 + DEFB %01000010 + DEFB %01000010 + DEFB %01111100 + DEFB %01000100 + DEFB %01000010 + DEFB %00000000 + +; $53 - Character: 'S' CHR$(83) + + DEFB %00000000 + DEFB %00111100 + DEFB %01000000 + DEFB %00111100 + DEFB %00000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $54 - Character: 'T' CHR$(84) + + DEFB %00000000 + DEFB %11111110 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00000000 + +; $55 - Character: 'U' CHR$(85) + + DEFB %00000000 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %00111100 + DEFB %00000000 + +; $56 - Character: 'V' CHR$(86) + + DEFB %00000000 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %00100100 + DEFB %00011000 + DEFB %00000000 + +; $57 - Character: 'W' CHR$(87) + + DEFB %00000000 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %01000010 + DEFB %01011010 + DEFB %00100100 + DEFB %00000000 + +; $58 - Character: 'X' CHR$(88) + + DEFB %00000000 + DEFB %01000010 + DEFB %00100100 + DEFB %00011000 + DEFB %00011000 + DEFB %00100100 + DEFB %01000010 + DEFB %00000000 + +; $59 - Character: 'Y' CHR$(89) + + DEFB %00000000 + DEFB %10000010 + DEFB %01000100 + DEFB %00101000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00000000 + +; $5A - Character: 'Z' CHR$(90) + + DEFB %00000000 + DEFB %01111110 + DEFB %00000100 + DEFB %00001000 + DEFB %00010000 + DEFB %00100000 + DEFB %01111110 + DEFB %00000000 + +; $5B - Character: '[' CHR$(91) + + DEFB %00000000 + DEFB %00001110 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001110 + DEFB %00000000 + +; $5C - Character: '\' CHR$(92) + + DEFB %00000000 + DEFB %00000000 + DEFB %01000000 + DEFB %00100000 + DEFB %00010000 + DEFB %00001000 + DEFB %00000100 + DEFB %00000000 + +; $5D - Character: ']' CHR$(93) + + DEFB %00000000 + DEFB %01110000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %01110000 + DEFB %00000000 + +; $5E - Character: '^' CHR$(94) + + DEFB %00000000 + DEFB %00010000 + DEFB %00111000 + DEFB %01010100 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00000000 + +; $5F - Character: '_' CHR$(95) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %11111111 + +; $60 - Character: 'ukp' CHR$(96) + + DEFB %00000000 + DEFB %00011100 + DEFB %00100010 + DEFB %01111000 + DEFB %00100000 + DEFB %00100000 + DEFB %01111110 + DEFB %00000000 + +; $61 - Character: 'a' CHR$(97) + + DEFB %00000000 + DEFB %00000000 + DEFB %00111000 + DEFB %00000100 + DEFB %00111100 + DEFB %01000100 + DEFB %00111100 + DEFB %00000000 + +; $62 - Character: 'b' CHR$(98) + + DEFB %00000000 + DEFB %00100000 + DEFB %00100000 + DEFB %00111100 + DEFB %00100010 + DEFB %00100010 + DEFB %00111100 + DEFB %00000000 + +; $63 - Character: 'c' CHR$(99) + + DEFB %00000000 + DEFB %00000000 + DEFB %00011100 + DEFB %00100000 + DEFB %00100000 + DEFB %00100000 + DEFB %00011100 + DEFB %00000000 + +; $64 - Character: 'd' CHR$(100) + + DEFB %00000000 + DEFB %00000100 + DEFB %00000100 + DEFB %00111100 + DEFB %01000100 + DEFB %01000100 + DEFB %00111100 + DEFB %00000000 + +; $65 - Character: 'e' CHR$(101) + + DEFB %00000000 + DEFB %00000000 + DEFB %00111000 + DEFB %01000100 + DEFB %01111000 + DEFB %01000000 + DEFB %00111100 + DEFB %00000000 + +; $66 - Character: 'f' CHR$(102) + + DEFB %00000000 + DEFB %00001100 + DEFB %00010000 + DEFB %00011000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00000000 + +; $67 - Character: 'g' CHR$(103) + + DEFB %00000000 + DEFB %00000000 + DEFB %00111100 + DEFB %01000100 + DEFB %01000100 + DEFB %00111100 + DEFB %00000100 + DEFB %00111000 + +; $68 - Character: 'h' CHR$(104) + + DEFB %00000000 + DEFB %01000000 + DEFB %01000000 + DEFB %01111000 + DEFB %01000100 + DEFB %01000100 + DEFB %01000100 + DEFB %00000000 + +; $69 - Character: 'i' CHR$(105) + + DEFB %00000000 + DEFB %00010000 + DEFB %00000000 + DEFB %00110000 + DEFB %00010000 + DEFB %00010000 + DEFB %00111000 + DEFB %00000000 + +; $6A - Character: 'j' CHR$(106) + + DEFB %00000000 + DEFB %00000100 + DEFB %00000000 + DEFB %00000100 + DEFB %00000100 + DEFB %00000100 + DEFB %00100100 + DEFB %00011000 + +; $6B - Character: 'k' CHR$(107) + + DEFB %00000000 + DEFB %00100000 + DEFB %00101000 + DEFB %00110000 + DEFB %00110000 + DEFB %00101000 + DEFB %00100100 + DEFB %00000000 + +; $6C - Character: 'l' CHR$(108) + + DEFB %00000000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00001100 + DEFB %00000000 + +; $6D - Character: 'm' CHR$(109) + + DEFB %00000000 + DEFB %00000000 + DEFB %01101000 + DEFB %01010100 + DEFB %01010100 + DEFB %01010100 + DEFB %01010100 + DEFB %00000000 + +; $6E - Character: 'n' CHR$(110) + + DEFB %00000000 + DEFB %00000000 + DEFB %01111000 + DEFB %01000100 + DEFB %01000100 + DEFB %01000100 + DEFB %01000100 + DEFB %00000000 + +; $6F - Character: 'o' CHR$(111) + + DEFB %00000000 + DEFB %00000000 + DEFB %00111000 + DEFB %01000100 + DEFB %01000100 + DEFB %01000100 + DEFB %00111000 + DEFB %00000000 + +; $70 - Character: 'p' CHR$(112) + + DEFB %00000000 + DEFB %00000000 + DEFB %01111000 + DEFB %01000100 + DEFB %01000100 + DEFB %01111000 + DEFB %01000000 + DEFB %01000000 + +; $71 - Character: 'q' CHR$(113) + + DEFB %00000000 + DEFB %00000000 + DEFB %00111100 + DEFB %01000100 + DEFB %01000100 + DEFB %00111100 + DEFB %00000100 + DEFB %00000110 + +; $72 - Character: 'r' CHR$(114) + + DEFB %00000000 + DEFB %00000000 + DEFB %00011100 + DEFB %00100000 + DEFB %00100000 + DEFB %00100000 + DEFB %00100000 + DEFB %00000000 + +; $73 - Character: 's' CHR$(115) + + DEFB %00000000 + DEFB %00000000 + DEFB %00111000 + DEFB %01000000 + DEFB %00111000 + DEFB %00000100 + DEFB %01111000 + DEFB %00000000 + +; $74 - Character: 't' CHR$(116) + + DEFB %00000000 + DEFB %00010000 + DEFB %00111000 + DEFB %00010000 + DEFB %00010000 + DEFB %00010000 + DEFB %00001100 + DEFB %00000000 + +; $75 - Character: 'u' CHR$(117) + + DEFB %00000000 + DEFB %00000000 + DEFB %01000100 + DEFB %01000100 + DEFB %01000100 + DEFB %01000100 + DEFB %00111000 + DEFB %00000000 + +; $76 - Character: 'v' CHR$(118) + + DEFB %00000000 + DEFB %00000000 + DEFB %01000100 + DEFB %01000100 + DEFB %00101000 + DEFB %00101000 + DEFB %00010000 + DEFB %00000000 + +; $77 - Character: 'w' CHR$(119) + + DEFB %00000000 + DEFB %00000000 + DEFB %01000100 + DEFB %01010100 + DEFB %01010100 + DEFB %01010100 + DEFB %00101000 + DEFB %00000000 + +; $78 - Character: 'x' CHR$(120) + + DEFB %00000000 + DEFB %00000000 + DEFB %01000100 + DEFB %00101000 + DEFB %00010000 + DEFB %00101000 + DEFB %01000100 + DEFB %00000000 + +; $79 - Character: 'y' CHR$(121) + + DEFB %00000000 + DEFB %00000000 + DEFB %01000100 + DEFB %01000100 + DEFB %01000100 + DEFB %00111100 + DEFB %00000100 + DEFB %00111000 + +; $7A - Character: 'z' CHR$(122) + + DEFB %00000000 + DEFB %00000000 + DEFB %01111100 + DEFB %00001000 + DEFB %00010000 + DEFB %00100000 + DEFB %01111100 + DEFB %00000000 + +; $7B - Character: '{' CHR$(123) + + DEFB %00000000 + DEFB %00001110 + DEFB %00001000 + DEFB %00110000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001110 + DEFB %00000000 + +; $7C - Character: '|' CHR$(124) + + DEFB %00000000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00001000 + DEFB %00000000 + +; $7D - Character: '}' CHR$(125) + + DEFB %00000000 + DEFB %01110000 + DEFB %00010000 + DEFB %00001100 + DEFB %00010000 + DEFB %00010000 + DEFB %01110000 + DEFB %00000000 + +; $7E - Character: '~' CHR$(126) + + DEFB %00000000 + DEFB %00010100 + DEFB %00101000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + +; $7F - Character: '(c)' CHR$(127) + + DEFB %00111100 + DEFB %01000010 + DEFB %10011001 + DEFB %10100001 + DEFB %10100001 + DEFB %10011001 + DEFB %01000010 + DEFB %00111100 + + END + + diff --git a/Shared_Includes b/Shared_Includes new file mode 160000 index 0000000..6e27011 --- /dev/null +++ b/Shared_Includes @@ -0,0 +1 @@ +Subproject commit 6e27011b6259b0256ce65d91a53ef1185223eafa diff --git a/TRDOS/TR_MSD_2.ASZ b/TRDOS/TR_MSD_2.ASZ new file mode 100644 index 0000000..5677375 --- /dev/null +++ b/TRDOS/TR_MSD_2.ASZ @@ -0,0 +1,609 @@ +; +; for INCLUDE +; MS-DOS system +; +;********************************** + +MSDOS_FIND_FILE: + PUSH DE ; ИМЯ ИСКОМОГО ФАЙЛА + CALL MSDOS_1ST_FL + POP DE + RET C +FIND_LOOP_ALL: + CALL MSDOS_KOMP_FL + RET Z ; ВОЗВРАТ - ФАЙЛ НАЙДЕН !! +MSDOS_FIND_NEXT: + CALL MSDOS_NEXT_FL + JR NC,FIND_LOOP_ALL + RET + +;*********************************************** +; +;*********************************************** +MSDOS_1ST_FL: + PUSH DE + CALL READ_BPB + CALL READ_1ST_DIR + POP DE +MSDOS_TEST_FL: + PUSH DE + DI + IN A,(PAGE3) + LD E,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,E + LD E,(HL) + OUT (PAGE3),A + EI + LD A,E + POP DE + AND A + SCF + RET Z ; КОНЕЦ КАТАЛОГА + CP 0E5H + SCF + CCF +; DEC A ; СТЕРТЫЙ ФАЙЛ +; AND A + RET +;********************************** +MSDOS_NEXT_FL: + PUSH DE + LD DE,20H + ADD HL,DE + DI + IN A,(PAGE3) + LD D,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(COUNT_FL) + DEC A + LD (COUNT_FL),A + LD E,A + LD A,D + OUT (PAGE3),A +; RET_PAGE3 + EI + LD A,E + POP DE + AND A ; СБРОС CF !! + JR NZ,MSDOS_TEST_FL + PUSH DE + DI + IN A,(PAGE3) + LD D,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(COUNT_SEC) + DEC A + LD (COUNT_SEC),A + LD E,A + LD A,D + OUT (PAGE3),A +; RET_PAGE3 + EI + LD A,E + POP DE + AND A + SCF + RET Z + PUSH DE + CALL READ_NEXT_DIR + POP DE + LD HL,MS_DIR + JR MSDOS_TEST_FL + +;********************************** +MSDOS_CAT: + PUSH DE + CALL AUTO_1D97 ; CLS + CALL OPEN_CH2 + LD A,13 + RST 10H + POP DE ; СТРОКА ПОСЛЕ CAT + +CAT_SPACES: + LD A,(DE) + CP 20H + JR NZ,CAT_NO_SPACE + INC DE + JR CAT_SPACES +CAT_NO_SPACE: + CP 13 + JR NZ,CAT_ALL + LD DE,CAT_FILES +CAT_ALL: +; CALL MSDOS_FIND_FILE + CALL MSDOS_1ST_FL + JR C,CAT_END +CAT_LOOP: + JR Z,CAT_NO_PRINT + CALL MSDOS_KOMP_FL + JR NZ,CAT_NO_PRINT + PUSH HL + PUSH DE + CALL OPEN_CH2 + POP DE + POP HL +; CALL COPY_NAME_MSD + CALL PRINT_MSD_NAME + CALL PRINT_MSD_DATS +; CALL MSDOS_FIND_NEXT +CAT_NO_PRINT: + CALL MSDOS_NEXT_FL + JR NC,CAT_LOOP +CAT_END: + CALL OPEN_CH0 + JP AUTO_03E1 + +PRINT_MSD_NAME: + PUSH HL + PUSH DE + LD HL,FL_NAME + LD A,' ' + RST 10H + LD A,' ' + RST 10H + LD B,8 +PRINT_MSD_L1: + LD A,(HL) + RST 10H + INC HL + DJNZ PRINT_MSD_L1 + LD A,' ' + RST 10H + LD B,3 +PRINT_MSD_L2: + LD A,(HL) + RST 10H + INC HL + DJNZ PRINT_MSD_L2 + LD A,6 + RST 10H + + POP DE + POP HL + RET + +PRINT_MSD_DATS: + PUSH HL + PUSH DE + LD HL,FL_PLACE+1 + LD A,(HL) + DEC HL + CALL PRINT_HEX_A + LD A,(HL) + DEC HL + CALL PRINT_HEX_A + LD A,(HL) + DEC HL + CALL PRINT_HEX_A + LD A,' ' + RST 10H + LD A,(HL) + DEC HL + CALL PRINT_HEX_A + LD A,(HL) + DEC HL + CALL PRINT_HEX_A + LD A,13 + RST 10H + POP DE + POP HL + RET +;********************************************** +CAT_FILES: DB '*.*',13 +;********************************************** +COPY_NAME_MSD: + PUSH HL + PUSH DE + DI + IN A,(PAGE3) + LD C,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,C + LD DE,FL_NAME + LD BC,11 + LDIR + + LD BC,15 + ADD HL,BC + LD BC,5 + LDIR + + LD L,(HL) + OUT (PAGE3),A +; RET_PAGE3 + EI + LD A,L + POP DE + POP HL + AND A ; A = 0 ДЛЯ ФАЙЛА ДЛИНОЙ < 16 Мб. + RET +;********************************************* + +;****************************************************** +CMD_MSDOS: ; MS-DOS командер !! +;****************************************************** + CALL SINTAX_RET + LD HL,WORDS +LOOP_FIND2: + LD DE,(ADR_DOS_COM) + INC DE ; Пропустить / + + LD A,(HL) + AND A + JR Z,END_TAB + LD B,A + INC HL +LOOP_FIND: + LD A,(DE) + AND 223 + CP (HL) + JR NZ,NEXT_WORD + INC DE + INC HL + DJNZ LOOP_FIND + LD A,(DE) + CP 13 + JR Z,CMD_EXE1 + CP 20H + JR Z,CMD_EXE + JR NEXT_WORD2 +CMD_EXE: + INC DE +CMD_EXE1: + LD A,(HL) + INC HL + LD H,(HL) + LD L,A + CALL JP_HL + JP AUTO_03E1 +JP_HL: + JP (HL) + +NEXT_WORD: + INC HL + DJNZ NEXT_WORD +NEXT_WORD2: + INC HL + INC HL + JR LOOP_FIND2 + +END_TAB: + INC DE + LD A,(DE) + CP ':' + DEC DE + JR NZ,END_TAB3 + LD A,(DE) + AND 223 + SUB 'A' + JR C,END_TAB3 + CP 4 + JR NC,END_TAB2 + LD (OPER_DISK),A + LD (CONT_DISK),A + LD B,A + LD A,(PDOS_COPY) + AND 7CH + OR B + LD (PDOS_COPY),A + OUT (P_DOS_FF),A + JP AUTO_03E1 + +END_TAB2: + SUB 4 + CP RAMD_KEY_NUM + JR NC,END_TAB3 + CALL RAMD_SET1 + JP AUTO_03E1 + +END_TAB3: + LD A,0 + JP LOAD_RMD_XX + +MSD_ERR: + LD HL,MSDOS_MSG + JP AUTO_03E4 + +;************************************ + +LOAD_RMD: + CALL RAMD_SYM ; получить номер RAM-Disk из (DE) + JP C,MSD_ERR +LOAD_RMD_XX: + LD (MED_START+1),A ; сохранить + CALL MSDOS_FIND_FILE + JP C,MSD_ERR + + CALL RMD_BLKS_FL ; B - число блоков , DE - абсолютный сектор + LD A,(MED_START+1) + PUSH AF + CALL CLEAR_RMD_A ; освободить RAM-Disk A + POP AF + CALL GET_RMD_A ; установить RAM-Disk A размером B + + LD HL,0C000H + CALL MSDOS_READ_FILE + JP AUTO_03E1 + +SAVE_RMD: + CALL RAMD_SYM ; получить номер RAM-Disk из (DE) + JP C,MSD_ERR + LD (MED_START+1),A + CALL MSDOS_FIND_FILE + JP C,MSD_ERR + + CALL RMD_BLKS_FL + + LD A,(MED_START+1) + CALL TEST_RAMD ; проверить наличие RAM-Disk A размером B + JP C,MSD_ERR ; A - номер первого блока + JP Z,MSD_ERR + + LD HL,0C000H + CALL MSDOS_WRITE_FILE + JP AUTO_03E1 + +RMD_BLKS_FL: + LD A,(FL_SIZE+2) + PUSH HL + LD HL,(FL_SIZE) + LD B,A + AND A + RL L + RL H + RL B + AND A + RL L + RL H + RL B + LD A,H + OR L + JR Z,RMD_BLKS_L2 + INC B +RMD_BLKS_L2: + POP HL + RET ; B - ЧИСЛО БЛОКОВ RAM_DISK + +;**************************************** +MSDOS_KOMP_FL: ; DE - ИМЯ ФАЙЛА СО *,? + ; HL - ИМЯ В КАТАЛОГЕ + CALL COPY_NAME_MSD ; ПЕРЕМЕСТИТЬ ИМЯ В ОБЛАСТЬ TR-DOS + LD A,(FL_NAME) + AND A + JR NZ,KOMP_CONT1 + LD A,2 ; КОНЕЦ КАТАЛОГА + AND A ; NZ - ИМЯ НЕ СОВПАДАЕТ + RET + +KOMP_CONT1: + CP 0A5h + JR NZ,KOMP_CONT2 + LD A,1 ; СТЕРТЫЙ ФАЙЛ - ИМЯ НЕ СОВПАДАЕТ + AND A + RET + +KOMP_CONT2: + PUSH DE + PUSH HL + LD HL,FL_NAME + CALL MSDOS_KOMP_ALL + POP HL + POP DE + RET + +MSDOS_KOMP_ALL: + LD A,(DE) + INC DE + CP 20H + JR Z,MSDOS_KOMP_ALL + CP 13 + JR Z,KOMP_ERROR_NAME ; НЕВЕРНОЕ ИМЯ + DEC DE + + LD B,8 + CALL KOMP_TX + RET NZ + + LD A,(DE) + CP '.' + JR Z,KOMP_POINT + CP 13 + JR Z,KOMP_SPACING3 + CP 20H + JR Z,KOMP_SPACING3 +KOMP_ERROR_NAME: ; НЕВЕРНОЕ ИМЯ + LD A,1 + AND A + RET + +KOMP_POINT: + INC DE + + LD B,3 + CALL KOMP_TX + RET NZ + LD A,(DE) + CP 13 + RET Z + CP 20H + RET Z + JR KOMP_ERROR_NAME + +;**************************************************** +KOMP_TX: + LD A,(DE) + CP 13 + JR Z,KOMP_SPACING + CP 20H + JR Z,KOMP_SPACING + CP '.' + JR Z,KOMP_SPACING + CP '?' + JR Z,KOMP_EX_OK + CP '*' + JR Z,KOMP_EX_OK2 + CP 'a' + JR C,KOMP_EXEC + CP 'z'+1 + JR NC,KOMP_EXEC + AND 223 +KOMP_EXEC: + CP (HL) + RET NZ ; нет совпадения +KOMP_EX_OK: + INC DE +KOMP_EX_OK2: + INC HL + DJNZ KOMP_TX + CP '*' + JR NZ,KOMP_NO_STAR1 + INC DE +KOMP_NO_STAR1: + XOR A + RET + +KOMP_SPACING3: + LD B,3 +KOMP_SPACING: + LD A,20H +KOMP_L2: + CP (HL) + INC HL + RET NZ + DJNZ KOMP_L2 + RET + +;************************************************ + +CALC_CLAST: ; HL - стартовый кластер + PUSH BC + PUSH HL + DEC HL + DEC HL + DI + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(MS_BPB+CLAST_SIZE) + + EX DE,HL + LD BC,0 + + LD HL,0 + LD IX,0 + +CALC_CL_LP: + ADD IX,DE + ADC HL,BC + + DEC A + JR NZ,CALC_CL_LP + + LD DE,(MSD_DAT_SEC) + LD BC,(MSD_DAT_SEC2) + + ADD IX,DE + ADC HL,BC ; HL,IX - СМЕЩЕНИЕ КЛАСТЕРА ОТ НАЧАЛА ДАННЫХ + + EX AF,AF' + OUT (PAGE3),A + + PUSH IX + EX (SP),HL + POP IX + EX DE,HL + + EI + POP HL + POP BC + RET + +HD_TX: DB ' ERR_HD',0A0H +HD_TX2: DB 'HDD not found',0A0H +MSG_3: DB 'ERROR...',0A0H +MSDOS_MSG: DB 'File not found...',0A0H +NO_MEMORY: DB 'No RAM-Disk MEM',0A0H + +WORDS: + DB 5,'CLEAR' ; очистить RAM-Disk + DW CLEAR_RAMD_X + DB 3,'CAT' ; каталог MS-DOS + DW MSDOS_CAT + DB 3,'DIR' ; каталог MS-DOS + DW MSDOS_CAT + DB 3,'HDD' ; подключить HDD + DW HDD_SET + DB 3,'FDD' ; подключить FDD + DW FDD_SET + DB 3,'RMD' ; подключить RMD + DW RAMD_SET + DB 3,'HDT' ; тест HDD + DW HDD_PROG + DB 2,'RD' ; тест RAM-Disk space + DW RAMD_FAT_PROG + DB 4,'SAVE' ; сохранить RAM-Disk + DW SAVE_RMD + DB 4,'LOAD' ; загрузить RAM-Disk + DW LOAD_RMD + DB 4,'TBON' ; включить турбо + DW TB_ONOFF + DB 5,'TBOFF' ; выключить турбо + DW TB_ONOFF + DB 4,'INIT' ; инизиализация + DW INIT_RMD + DB 8,"SPRINTER" + DW SPR_1 + DB 2,"AY" + DW AY8910 + DB 0 + +;SAVE_AUTOEXEC: +; LD C,0FEH +; CALL EXP_FNS +; RET +AY8910: + LD A,0EAH + LD C,0F3H + CALL EXP_FNS + EI + CALL AUTO_1D97 + AND A + RET + +SPR_1: + LD A,(DE) + INC DE + CP 20H + JR Z,SPR_1 + CP "1" + LD C,0F0H + JR Z,SP_1 + CP "2" + SCF + RET NZ + LD C,0F1H +SP_1: LD A,(DE) + CP 21H + CCF + RET C + CALL EXP_FNS + EI + CALL AUTO_1D97 + AND A + RET + + +;**************************************************** +;***************************************** +; ERRORS +;***************************************** +; 0 - ГЛАВНАЯ ПРОВЕРКА ПРОГРАММЫ +; 1 - НЕВЕРНЫЙ ФОРМАТ ИМЕНИ +; 2 - КОНЕЦ КАТАЛОГА +; diff --git a/TRDOS/TR_MSD_3.ASZ b/TRDOS/TR_MSD_3.ASZ new file mode 100644 index 0000000..4cc80f0 --- /dev/null +++ b/TRDOS/TR_MSD_3.ASZ @@ -0,0 +1,596 @@ + +INIT_RMD: + LD C,9FH + CALL EXP_FNS + EI + RET + +; DI +; LD C,PAGE3 +; IN B,(C) +; LD A,SYS_PAGE +; OUT (C),A +; LD A,(0C000H) +; CPL +; LD (0C000H),A +; OUT (C),B +; EI +; CALL INIT_SYS_PAGE +; JP AUTO_03E1 + +;INIT_SYS_PAGE: +; LD C,97H +; CALL EXP_FNS +; EI +; RET + +RAMD_FAT_PROG: + CALL AUTO_1D97 ; CLS + CALL OPEN_CH2 + LD HL,RAMD_FAT +RAMD_F_L1: + DI + IN A,(PAGE3) + LD C,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,C + LD C,(HL) + OUT (PAGE3),A +; RET_PAGE3 + EI + PUSH HL + LD A,C + CALL PRINT_HEX_A + POP HL + INC L + JR NZ,RAMD_F_L1 + CALL OPEN_CH0 + JP AUTO_03E1 +;*************************************************************************** +TB_ONOFF: + DEC DE + DEC DE + LD A,(DE) + CP 'F' + JR Z,TB_OFF_EXE + CP 'f' + JR Z,TB_OFF_EXE +TB_ON_EXE: + LD C,8Fh + LD A,4 + JP EXP_FNS +TB_OFF_EXE: + LD C,8Fh + LD A,3 + JP EXP_FNS +; +;*************************************************************************** +; +; Алгоритм чтения файла +; +;*************************************************************************** +; Вход - данные файла в переменных TR-DOS - длина,стартовый кластер +; адрес загрузки, начальный блок загрузки +; предварительное значение max2 - длина блока, флаг блоковой +; загрузки +;*************************************************************************** +MSDOS_WRITE_FILE: + LD (CONT_BUF_ADR),HL ; адрес загрузки + LD (MED_START),A ; страница загрузки + LD A,6 + JR MSDOS_R_W_FILE +; +MSDOS_READ_FILE: + LD (CONT_BUF_ADR),HL ; адрес загрузки + LD (MED_START),A ; страница загрузки + LD A,5 +MSDOS_R_W_FILE: + LD (MED_LEN),A +; beg=start_claster ; Получить начальный кластер + LD IX,(START_CLASTER) ; стартовый кластер +; next=beg +; max=file_len ; длина в кластерах + CALL GET_FILE_CLASTERS_BC +; LD A,B +; CALL PRINT_HEX_A +; LD A,C +; CALL PRINT_HEX_A +msd_rf_cont2: +; max2=blk_len ; длина блока в кластерах + +; DI ; GET_BLK_CLASTERS_L +; IN A,(PAGE3) +; EX AF,AF' +; LD A,SYS_PAGE +; OUT (PAGE3),A +; LD A,(C_P_B) +; LD L,A +; EX AF,AF' +; OUT (PAGE3),A +; EI + + LD L,128 + +msd_rf_cont1: +; num=0 + PUSH IX + POP DE + LD A,0 +;--------------------------------- +msd_rf_cont: +; num=num+1 + INC A +; if num=max then goto msd_rf_last_blk ; Проверить число кластеров, + CP C + JR NZ,msd_rf_l1 + DEC B + INC B + JR Z,msd_rf_last_blk +msd_rf_l1: +; if num=max2 then goto msd_rf_blk ; Проверить число кластеров, + CP L + PUSH HL + PUSH BC + PUSH AF + + JR Z,msd_rf_blk +; sec=next ; читаемых за один раз +; next=FAT(sec) ; Вычислить следующий кластер + CALL FAT_DE_to_HL +; if next = end then goto msd_rf_last_blk ; проверить конец + JR C,msd_rf_last_blk2 + EX DE,HL + AND A + INC HL +; if next-sec = 1 then goto msd_rf_cont ; Проверить next + SBC HL,DE + JR NZ,msd_rf_blk2 + + POP AF + POP BC + POP HL + JR msd_rf_cont +;--------------------------------- +msd_rf_blk: + CALL FAT_DE_to_HL + JR C,msd_rf_last_blk2 + EX DE,HL +msd_rf_blk2: +; beg=next +; EX DE,IX + PUSH IX + PUSH DE + POP IX + POP DE + + POP AF + PUSH AF + call READ_num_clasters_from_beg ; читать с кластера DE + POP AF + POP BC + POP HL + LD H,A + +; max=max-num + LD A,C + SUB H + LD C,A + JR NC,msd_rf_l2 + DEC B +msd_rf_l2: + +; max2=max2-num ; блок окончен ? + LD A,L + SUB H + LD L,A +; if not max2=0 then goto msd_rf_cont1 ; если да,следующий блок + JR NZ,msd_rf_cont1 +; max2=blk_len ; длина блока в кластерах для чтения + JR msd_rf_cont2 +;------------------------------- +msd_rf_last_blk2: + POP AF + POP BC + POP HL +msd_rf_last_blk: +; EX DE,IX + push de + push ix + pop de + pop ix + call READ_num_clasters_from_beg + RET +;========================================= +READ_num_clasters_from_beg: + PUSH IX + PUSH DE + + CALL CLAST_TO_SEC + + PUSH AF + LD A,(MED_LEN) + LD C,A + SUB 5 + JR Z,CONT_XX1 + DEC A + JR NZ,SKEEP_XX1 +CONT_XX1: + POP AF + + ADD A,A + INC A + +LOOP_128: + + PUSH AF + PUSH DE + PUSH IX + PUSH BC + + LD HL,(CONT_BUF_ADR) + LD A,(MED_START) + + CALL MSD_R_W_UT + + LD (CONT_BUF_ADR),HL + LD (MED_START),A + + POP BC + POP IX + POP DE + POP AF + + LD H,0 + LD L,B + AND A + ADC HL,DE + JR NC,NO_INC_IX + INC IX +NO_INC_IX: + EX DE,HL + + LD B,128 + DEC A + JR NZ,LOOP_128 + + POP DE + POP IX + RET + +SKEEP_XX1: + POP AF + + POP DE + POP IX + RET +;***************************************** +MADE_next_blk: + PUSH BC + LD C,95h + CALL EXP_FNS + POP BC + INC L ; SET ZF, if END BLK !!! + LD HL,0C000H + RET +;***************************************** + +CLAST_TO_SEC: + PUSH AF + EX DE,HL + CALL CALC_CLAST + + DI + IN A,(PAGE3) + LD B,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(MS_BPB+CLAST_SIZE) + LD C,A + LD A,B + OUT (PAGE3),A + EI + + POP AF + + PUSH HL + LD L,A + LD H,0 + + LD A,C +CLAST_TO_SEC_L: + RRA + JR C,CLAST_TO_SEC_L2 + ADD HL,HL + JR CLAST_TO_SEC_L + +CLAST_TO_SEC_L2: + LD B,L + LD A,H + POP HL + + RET + + +FAT_DE_to_HL: +; LD H,D +; LD L,E +; INC HL +; AND A +; RET + + PUSH AF + PUSH DE + PUSH BC + PUSH IX + DI + IN A,(PAGE3) + LD C,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,C + LD HL,(FAT_FLAG) + LD BC,(MS_BPB+SEC_SIZE) ; размер сектора + OUT (PAGE3),A + EI + + PUSH HL + LD A,H + AND 80H + LD C,A + LD H,D + LD L,E + + ADD HL,DE + ADC A,C + JR NC,FAT_12_L1 + ADD HL,DE + ADC A,C +FAT_12_L1: + ADD HL,DE + ADC A,C + + RRA ; A,HL = FATx4..3/2 + RR H + RR L + + POP DE ; восстановить флаги FAT в DE + PUSH AF ; запомнить флаг смещения + ; HL - смещение от начала FAT + +; A,HL - смещение в FAT + PUSH HL + LD C,0 + PUSH BC + +FAT_DE_L1: + RR B + JR C,FAT_DE_L2 + RRA + RR H + JR FAT_DE_L1 + +FAT_DE_L2: + + LD A,H ; A - сектор FAT + BIT 0,D + JR NZ,CALL_ALL + CP E ; номер ранее считанного сектора FAT +CALL_ALL: + CALL NZ,READ_FAT_SEC ; читать FAT сектор номер A + POP BC ; вспомнить размер сектора + POP HL ; вспомнить адрес в FAT + + DEC BC + LD A,H + AND B + LD H,A + + LD BC,MS_FAT + ADD HL,BC + DI + IN A,(PAGE3) + LD C,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,C + LD (FAT_FLAG),DE + LD C,(HL) ; считать кластер + INC HL + LD B,(HL) + INC HL + OUT (PAGE3),A +; RET_PAGE3 + EI + POP AF + JR NC,FAT_HALF + LD A,4 +FAT_HALF_L: + RR B + RR C + DEC A + JR NZ,FAT_HALF_L +FAT_HALF: + BIT 7,D + LD A,0FFH + JR NZ,FAT_16_L2 + LD A,0FH + AND B + LD B,A + LD A,0FH +FAT_16_L2: + CP B + JR NZ,NO_END_CLAST +FAT_ALL: + LD A,C + CP 0F0H + JR C,NO_END_CLAST + LD L,C + LD H,B + POP IX + POP BC + POP DE + POP AF + SCF + RET + + BLOCK #09FF-$, #FF + BLOCK #0A01-$, #FF + +NO_END_CLAST: +; PUSH AF +; LD A,B +; CALL PRINT_HEX_A +; LD A,C +; CALL PRINT_HEX_A +; POP AF + LD H,B + LD L,C + POP IX + POP BC + POP DE + POP AF + AND A + RET + +ERROR_FAT: + LD HL,MSG_3 ; ERROR + JP AUTO_03E4 + +;********************************************** + +READ_FAT_SEC: + PUSH IX + PUSH HL + PUSH BC + LD E,A + RES 0,D + PUSH DE + LD D,0 + + DI + IN A,(PAGE3) + LD L,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,L + LD IX,(MSD_FAT_SEC) + LD HL,(MSD_FAT_SEC2) + OUT (PAGE3),A + EI + + LD BC,0 + ADD IX,DE + ADC HL,BC + + PUSH IX + POP DE + PUSH HL + POP IX + + LD HL,(CONT_BUF_ADR) + LD A,(MED_START) + PUSH AF + PUSH HL + + LD HL,MS_FAT + LD A,SYS_PAGE + LD B,2 + LD C,5 + CALL MSD_R_W_UT + + POP HL + POP AF + LD (CONT_BUF_ADR),HL + LD (MED_START),A + + POP DE + POP BC + POP HL + POP IX + RET +;********************************************** +; +; Считывание FAT по три сектора ??? +; +;********************************************** + +GET_FILE_CLASTERS_BC: + DI + IN A,(PAGE3) + LD L,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,L + LD HL,(CLASTER_LEN) + OUT (PAGE3),A +; RET_PAGE3 + EI + LD BC,(FL_SIZE) + LD A,(FL_SIZE+2) + LD DE,0 + SCF +GET_FL_CL_2: ; вычисление количества кластеров в файле + RR H ; HL/2 + RR L + JR C,GET_FL_L2 ; если первый бит = 1 - выйти из цикла + RRA ; file_len/2 + RR B + RR C + JR NC,GET_FL_CL_2 + LD E,1 + JR GET_FL_CL_2 +GET_FL_L2: + BIT 0,E + RET Z + INC BC + RET +;*************************************************************************** + +HDD_PROG: + LD C,0 + CALL HD_CMD + JP C,HDD_PROG_E + LD HL,HD_IDF_ADR+54 + LD B,32 +HDD_PROG_L: + DI + IN A,(PAGE3) + LD D,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,L + XOR 1 + LD L,A + LD C,(HL) + LD A,L + XOR 1 + LD L,A + INC HL + LD A,D + OUT (PAGE3),A +; RET_PAGE3 + EI + LD A,C + PUSH BC + RST 10H + POP BC + DJNZ HDD_PROG_L + LD C,1 + CALL HD_CMD + JP NC,AUTO_03E1 + +HDD_ERROR: + CALL PRINT_HEX_A + LD HL,HD_TX + JP AUTO_03E4 + +; JP AUTO_03E1 + +HDD_PROG_E: + LD HL,HD_TX2 + JP AUTO_03E4 +; +;*************************************************************************** + + diff --git a/TRDOS/TR_MSD_S.ASZ b/TRDOS/TR_MSD_S.ASZ new file mode 100644 index 0000000..bb1fed1 --- /dev/null +++ b/TRDOS/TR_MSD_S.ASZ @@ -0,0 +1,768 @@ +; +; for INCLUDE +; MS-DOS system +; +;******************************************** +; MS-DOS utilites +;******************************************** + +;************************************************ +;* Программа чтения/записи MS-DOS дисков * +;* 1. Адрес буфера - HL * +;* 2. Номер сектора - DE * +;* 3. Число секторов - B * +;* 4. Команда - C * +;* 5. Страница ОЗУ - A * +;************************************************ + +HDD_REDIR: + POP AF + CALL HD_CMD + RET NC + JP HDD_ERROR + +MSD_R_W_UT: + PUSH AF + CALL DISK_REDIR + CP 40H + JR NC,HDD_REDIR + POP AF + EX AF,AF' + LD A,C + SUB 4 + JR Z,MSD_cmd4 +; JR Z,MSD_cmd4M + DEC A + JR Z,MSD_cmd5 +; JR Z,MSD_cmd5M + DEC A + JR Z,MSD_cmd6 +; JR Z,MSD_cmd6M + EX AF,AF' + RET + +;MSD_cmd4M: +; LD DE,0 +; LD IX,0 +; LD B,1 +;MSD_cmd5M: +; EX DE,HL +; PUSH IX +; EX (SP),HL +; POP IX +; LD C,12H +; LD A,(OPER_DISK) +; DI +; PUSH IY +; CALL HD_CMD +; POP IY +; EI +; PUSH IX +; EX (SP),HL +; POP IX +; EX DE,HL +; JP C,ERROR_MSDOS +; EX AF,AF' +; AND A +; RET +; +;MSD_cmd6M: +; EX DE,HL +; PUSH IX +; EX (SP),HL +; POP IX +; LD C,13H +; LD A,(OPER_DISK) +; DI +; PUSH IY +; CALL HD_CMD +; POP IY +; EI +; PUSH IX +; EX (SP),HL +; POP IX +; EX DE,HL +; JP C,ERROR_MSDOS +; EX AF,AF' +; AND A +; RET + + +;************************************* + +MSD_cmd4: + PUSH HL + PUSH AF + CALL RESET_WG + POP AF + POP HL + LD B,1 + LD DE,0 +MSD_cmd5: + LD A,0 + LD (RD_WR_COM),A + EX AF,AF' + JR MSD_RWU1 +MSD_cmd6: + LD A,255 + LD (RD_WR_COM),A + EX AF,AF' + JR MSD_RWU1 + +PREPARE1: + LD (MED_START),A + LD (CONT_BUF_ADR),HL + DI + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + + LD A,(DS_1440-4000H) + AND BIT_MASK_1440 + SET 0,A + LD (DS_1440-4000H),A + LD A,C + OUT (PAGE2),A + EI + CALL Z,RES_1440 + CALL NZ,SET_1440 + + CALL CALC_SECTOR + LD (CONT_SEC),DE + LD A,(RD_WR_COM) + RET + +MSD_RWU1: + PUSH BC + PUSH HL + CALL PREPARE1 +; JR MSD_R_W_LOOP + +MSD_R_W_LOOP: + POP HL + POP BC + LD A,(MED_START) + INC B + DEC B + LD DE,(CONT_SEC) + RET Z + + DEC B + PUSH BC + PUSH HL + LD (CONT_BUF_ADR),HL + LD A,(CONT_SEC) + LD (SEC_NUM),A + LD A,(CONT_TRK) + CALL Global_trk + LD A,(RD_WR_COM) + OR A + PUSH AF + CALL Z,MSD_3F0E + POP AF + CALL NZ,MSD_3F0A + DI + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD DE,(MS_BPB+SEC_SIZE-4000h) + LD A,C + OUT (PAGE2),A + EI + POP HL + ADD HL,DE + LD A,H + OR L + JR NZ,MSD_NO_PAGE + DI + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,(MED_START) + LD HL,RAMD_FAT-4000H + LD L,A + LD L,(HL) + LD A,C + OUT (PAGE2),A + EI + LD A,L + LD HL,0C000H + LD (MED_START),A +MSD_NO_PAGE: + LD (CONT_BUF_ADR),HL + PUSH HL + + DI + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,(DS_1440-4000H) + RES 0,A + LD (DS_1440-4000H),A + LD A,(MSD_SECS-4000h) + LD L,A + LD A,C + OUT (PAGE2),A + EI + LD A,L + LD HL,CONT_SEC + INC (HL) + CP (HL) + JP NZ,MSD_R_W_LOOP + LD (HL),0 + LD HL,CONT_TRK + INC (HL) + JP MSD_R_W_LOOP +;***************************************** +; +;***************************************** +MSD_3F0A: + LD A,160 + JR MSD_3F10 +MSD_3F0E: + LD A,128 +MSD_3F10: + LD (COMAND_WG),A +MSD_3F13: + LD D,5 ; ЧИСЛО ПОВТОРЕНИЙ +MSD_3F15: + PUSH DE + PUSH IX + DI + IN A,(PAGE3) + EX AF,AF' + LD A,(MED_START) + OUT (PAGE3),A + AND A + JR Z,NO_OFF_SCR + LD A,0C0H + OUT (RGADR),A +NO_OFF_SCR: + LD IX,MSD_RET_ADR + LD A,(SEC_NUM) + INC A + OUT (WG_SEC),A + LD HL,(CONT_BUF_ADR) + LD C,127 + LD A,(COMAND_WG) + OUT (WG_COM),A + CP 160 + JP Z,MSD_3FBA ; ЗАПИСЬ СЕКТОРА + JP MSD_3FD5 ; ЧТЕНИЕ СЕКТОРА +MSD_RET_ADR: + LD A,0 + OUT (RGADR),A + EX AF,AF' + OUT (PAGE3),A + POP IX + POP DE + EI + IN A,(WG_COM) + LD B,A + AND 127 + RET Z + +MSD_3F39: + LD HL,READ_ONLY_MSG + AND '@' + JR NZ,MSD_3F4B + LD A,B + AND 4 + JR Z,MSD_3FA0 ; -> ДРУГАЯ ОШИБКА + DEC D ; ПОТЕРЯ ДАННЫХ + JR Z,MSD_3F48 ; MSD_3F15 + CALL SWITCH_DT + JR MSD_3F15 +MSD_3F48: + LD HL,DISK_ERROR_MSG +MSD_3F4B: + LD A,208 + OUT (WG_COM),A + LD A,B + AND 1 + JP NZ,AUTO_3EE7 + + PUSH HL + CALL AUTO_1D97 + POP HL + RST PRINT_LN + LD HL,AUTO_2A13 + RST PRINT_LN + IN A,(WG_TRK) + CALL AUTO_1DA3 + LD HL,AUTO_2A18 + RST PRINT_LN + IN A,(WG_SEC) + CALL AUTO_1DA3 + LD HL,AUTO_29FE + RST PRINT_LN +MSD_3F7B: + CALL AUTO_1052 + CP 'I' + RET Z + CP 'R' + JR Z,MSD_3F94 + CP 'A' + JR NZ,MSD_3F7B + CALL exit_No_disk + LD A,7 + LD (DOS_ERR_2),A + JP AUTO_01D3 +MSD_3F94: + LD A,(CONT_TRK) + CALL Global_trk + CALL pause_750ms + JP MSD_3F13 +MSD_3FA0: + DEC D + JP Z,MSD_3F48 + CALL SWITCH_DT + PUSH DE + CALL AUTO_3D98 ; RESET_WG + LD A,(CONT_TRK) + CALL Global_trk ; ПОЗИЦИОНИРОВАНИЕ + POP DE + JP MSD_3F15 + + +SWITCH_DT: + DI + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,(DS_1440-4000H) + BIT 0,A + JR Z,NO_SW_DT + XOR BIT_MASK_1440 + LD (DS_1440-4000H),A + AND BIT_MASK_1440 + LD A,C + OUT (PAGE2),A + EI + CALL Z,RES_1440 + CALL NZ,SET_1440 + RET +NO_SW_DT: + LD A,C + OUT (PAGE2),A + EI + RET + +;****************************************** +; Sector Write !!! +;****************************************** +MSD_3FBA: + LD B,4 +MSD_3FBC: + IN A,(P_DOS_FF) + AND 192 + JR NZ,MSD_3FD1 + INC DE + LD A,E + OR D + JR NZ,MSD_3FBC + DJNZ MSD_3FBC +MSD_RET_IX: + JP (IX) +MSD_3FCA: + IN A,(P_DOS_FF) + AND 192 + JR Z,MSD_3FCA + JP M,MSD_RET_IX +MSD_3FD1: + OUTI + JR MSD_3FCA +;****************************************** +; Sector Read !!! +;****************************************** +MSD_3FD5: + LD B,4 +MSD_3FD7: + IN A,(P_DOS_FF) + AND 192 + JR NZ,MSD_3FEC + INC DE + LD A,E + OR D + JR NZ,MSD_3FD7 + DJNZ MSD_3FD7 +MSD_RET_IX2: + JP (IX) +MSD_3FE5: + IN A,(P_DOS_FF) + AND 192 + JR Z,MSD_3FE5 + JP M,MSD_RET_IX2 +MSD_3FEC: + INI + JR MSD_3FE5 +;****************************************** + +;**************************************** + +RES_1440: + LD A,01h + OUT (0BDh),A + RET + +SET_1440: + LD A,21h + OUT (0BDh),A + RET + +READ_BPB: + CALL DISK_REDIR + CP 40H + JR NC,HDD_REDIR2 + CP 4 + JP NC,ERROR_MSDOS + + CALL SET_DISK + + CALL MAIN_BPB + RET NC + JP ERROR_MSDOS + +HDD_REDIR2: + + CALL MAIN_BPB + RET NC + +SPACIAL_BPB: + LD IX,0 + LD DE,63 + LD A,SYS_PAGE + LD HL,MS_BPB + LD C,5 + LD B,1 + CALL MSD_R_W_UT + CALL BPB_PARAMS + RET NC + JP ERROR_MSDOS + +MAIN_BPB: + LD IX,0 + LD DE,0 + LD A,SYS_PAGE + LD HL,MS_BPB + LD C,4 + LD B,1 + CALL MSD_R_W_UT + CALL BPB_PARAMS + RET + +; LD C,18H +; LD A,(OPER_DISK) +; CALL HD_CMD + +; DI +; IN A,(PAGE3) +; EX AF,AF' +; LD A,SYS_PAGE +; OUT (PAGE3),A +; +; LD A,(MS_BPB+S_P_T) ; Количество секторов на трек +; LD L,A +; +; EX AF,AF' +; OUT (PAGE3),A +; EI +; +; LD A,(OPER_DISK) +; LD C,19H +; CALL HD_CMD + +BPB_PARAMS: + + DI + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + + LD A,(MS_BPB+FORM_CODE) + CP 0F0H + JP C,ERROR_BPB + + LD HL,MS_BPB+FAT_ID + LD DE,FAT1_MSG + LD B,4 +R_BPB_L1: + LD A,(DE) + CP ' ' + JR NZ,R_BPB_L11 + INC DE + JR R_BPB_L1 +R_BPB_L11: + CP (HL) + JR NZ,PC_DOS_BPB + + INC HL + INC DE + DJNZ R_BPB_L1 + LD A,(HL) + CP '6' + LD HL,81FFH ; FAT16 флаг + JR Z,BPB_FAT + CP '2' + JP NZ,ERROR_BPB + +PC_DOS_BPB: + LD HL,01FFH ; FAT12 флаг +BPB_FAT: + LD (FAT_FLAG),HL + + LD A,(MS_BPB+S_P_T) ; Количество секторов на трек + LD (MSD_SECS),A + + BIT 7,H + + LD IX,0 + LD HL,0 ; вычислить начальный сектор FAT + + JR Z,NO_LD_SPECIAL + LD IX,(MS_BPB+SPECIAL_SECS) + LD HL,(MS_BPB+SPECIAL_SECS+2) +NO_LD_SPECIAL: + + LD BC,0 + LD DE,(MS_BPB+RESERV_SECS) + + ADD IX,DE + ADC HL,BC + + LD (MSD_FAT_SEC),IX ; начальный сектор FAT + LD (MSD_FAT_SEC2),HL ; начальный сектор FAT + + LD DE,(MS_BPB+S_P_F) ; число секторов в FAT + LD BC,0 + + LD A,(MS_BPB+FATS_NUM) ; количество FATs +NEXT_ADD: + ADD IX,DE + ADC HL,BC + DEC A + JR NZ,NEXT_ADD + + LD (MSD_CAT_SEC),IX ; начальный сектор DIR + LD (MSD_CAT_SEC2),HL ; начальный сектор DIR + + LD BC,(MS_BPB+SEC_SIZE) ; Размер сектора + RL C + RL B + RL C + RL B + RL C + RL B + LD C,B + LD B,0 ; BC - число файловых записей в секторе + LD A,C + LD (F_P_S),A + + LD DE,(MS_BPB+FLS_NUM) ; Число файловых записей + + EX DE,HL + DEC HL + XOR A +NEXT_ADD2: + INC A + JR Z,ERROR_BPB + SBC HL,BC + JR NC,NEXT_ADD2 + EX DE,HL + + LD E,A ; A - число секторов в DIR + LD D,0 + LD BC,0 + LD (S_P_C),A + + ADD IX,DE ; Начало DATA area + ADC HL,BC + + LD (MSD_DAT_SEC),IX + LD (MSD_DAT_SEC2),HL + + LD BC,(MS_BPB+SEC_SIZE) ; Размер сектора + LD HL,0 + LD A,(MS_BPB+CLAST_SIZE) + LD D,A + LD A,0 +NEXT_ADD3: + ADD HL,BC ; Вычисление длины кластера + ADC A,0 + DEC D + JR NZ,NEXT_ADD3 + + LD (CLASTER_LEN),HL + LD B,0 + LD C,A + LD (CLASTER_LEN2),BC + + LD HL,0 + LD BC,(MS_BPB+S_P_T) + LD A,(MS_BPB+H_P_S) +BPB_L1: ; ВЫЧИСЛИТЬ КОЛИЧЕСТВО СЕКТОРОВ НА ЦИЛИНДР + ADD HL,BC + DEC A + JR NZ,BPB_L1 + LD (S_X_H),HL + + EX AF,AF' + OUT (PAGE3),A + EI + AND A + RET + +ERROR_BPB: + EX AF,AF' + OUT (PAGE3),A + SCF + EI + RET + + +ERROR_MSDOS: + LD HL,MSG_3 + JP AUTO_03E4 + +FAT1_MSG: DB 'FAT1' +;******************************************** +READ_NEXT_DIR: + DI + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD DE,(MSD_CONT_SEC) + LD IX,(MSD_CONT_SEC2) + JR READ_NEXT2 +;******************************************** +READ_1ST_DIR: + DI + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD IX,(MSD_CAT_SEC2) ; + LD DE,(MSD_CAT_SEC) ; НАЧАЛЬНЫЙ СЕКТОР КАТАЛОГА + LD (MSD_CONT_SEC),DE + LD (MSD_CONT_SEC2),IX + LD A,(S_P_C) ; СЕКТОРОВ В КАТАЛОГЕ + LD (COUNT_SEC),A +READ_NEXT2: + EX AF,AF' + OUT (PAGE3),A +; RET_PAGE3 + EI + LD HL,MS_DIR + LD A,SYS_PAGE + LD B,1 + LD C,5 + CALL MSD_R_W_UT + DI + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + + LD DE,(MSD_CONT_SEC) + INC DE + LD (MSD_CONT_SEC),DE + LD A,D + OR E + JR NZ,READ_NEXT3 + LD IX,(MSD_CONT_SEC2) + INC IX + LD (MSD_CONT_SEC2),IX +READ_NEXT3: + LD A,(F_P_S) ; ФАЙЛОВ В СЕКТОРЕ + LD (COUNT_FL),A + + EX AF,AF' + OUT (PAGE3),A +; RET_PAGE3 + EI + LD HL,MS_DIR + AND A + RET + +;****************************************** +;******************************************** +CALC_SECTOR: ; ВЫЧИСЛИТЬ ТРЕК/СЕКТОР для дискет + LD A,D ; DE - абс. номер сектора + OR E + JR NZ,CALC_SECT_L1 + LD HL,0 + RET + +CALC_SECT_L1: + PUSH BC + EX DE,HL + DI + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD BC,(MS_BPB+S_P_T) + XOR A +LOOP_TRK: + SBC HL,BC + JR C,SEC_ENDER + INC A + JR NZ,LOOP_TRK + POP BC + JP ERROR_BPB + +SEC_ENDER: + ADD HL,BC + LD H,A ; ТРЕК +; LD L,L ; СЕКТОР + EX AF,AF' + OUT (PAGE3),A + EI + EX DE,HL + POP BC + RET + +;************************************************ + +REDIR_LINE_CODING: + LD (MED_START),HL + LD HL,(DOS_CH_ADR) + LD A,(HL) + CP '/' + RET Z + JP AUTO_3057 + +PRINT_HEX_A: + PUSH AF + RRCA + RRCA + RRCA + RRCA + CALL PRINT_HEX_SYM + POP AF +PRINT_HEX_SYM: + PUSH HL + PUSH DE + PUSH BC + AND 0FH + ADD A,'0' + CP '9'+1 + JR C,NO_ADD + ADD A,7 +NO_ADD: + RST 10H + POP BC + POP DE + POP HL + RET + +;******************************************* + + diff --git a/TRDOS/TR_RMD_S.ASZ b/TRDOS/TR_RMD_S.ASZ new file mode 100644 index 0000000..ef8d6b2 --- /dev/null +++ b/TRDOS/TR_RMD_S.ASZ @@ -0,0 +1,480 @@ +DISK_REDIR: + PUSH HL + PUSH BC + LD A,(OPER_DISK) + AND 3 + LD C,0CFH + CALL EXP_FNS + EI + POP BC + POP HL + RET + +R_W_REDIR: + CALL DISK_REDIR + CP 4 + JR C,RECALL_1E70 + CP 20 + JR C,RECALL_RAMD +ERROR_DISK: + JP AUTO_3EE7 ; переход на ошибку диска + +RECALL_1E70: + CALL RES_1440 + LD A,(RD_WR_COM) + PUSH BC + PUSH HL + CALL READ_TRK_NUM + JP AUTO_1E70 + +RECALL_RAMD: + SUB 4 + CALL TEST_RAMD + JR C,ERROR_DISK + SCF + JR Z,ERROR_DISK + JP READ_WRITE_RAMD + +RECALL_MSD: + JR ERROR_DISK ; переход на ошибку диска + +TEST_RAMD: + PUSH HL + LD C,0CEH + CALL EXP_FNS + EI + POP HL + RET + +; ╔═══════════════════════════════════╗ +; ║ RD/WR SECTOR HL - BUFER ║\ +; ║ CONT_SEC - SEC/TRK FOR READ/WRITE ║\ +; ║ COM_RD_WR - команда чтение/запись ║\ +; ║ 0 - чтение, 255 - запись ║\ +; ╚═══════════════════════════════════╝\ +; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + +READ_WRITE_RAMD: + +; +; CALL DISK_REDIR +; SUB 4 +; RET C +; CP 16 +; CCF +; RET C +; +; PUSH HL +; LD HL,RAMD_KEYS +; ADD A,L +; LD L,A +; IN A,(PAGE3) +; LD C,A +; LD A,SYS_PAGE +; DI +; OUT (PAGE3),A +; LD L,(HL) +; LD A,C +; OUT (PAGE3),A +; EI +; LD A,L ; 1-st block +; POP HL +; +; AND A +; SCF +; RET Z +; EX AF,AF' +; +; IN A,(RGADR) +; PUSH AF +; +; PUSH HL +; PUSH DE +; PUSH BC +; +; LD DE,(CONT_SEC) +; LD A,D ; ФИЗИЧЕСКИЕ -> ЛОГИЧЕСКИЕ !!! +; RRCA +; RRCA +; RRCA +; RRCA +; LD D,A +; AND 0F0H +; OR E +; LD E,A +; LD A,D +; AND 0FH +; LD D,A +; +; LD A,(RD_WR_COM) +; AND A +; JR Z,RAMD_READ +; PUSH AF +; LD A,0C0H +; OUT (RGADR),A +; POP AF +;RAMD_READ: +; +; POP BC +; PUSH BC +; LD C,0C8H +; DI +; EX AF,AF' +; CALL EXP_FNS ; чтение/запись RAM-Disk +; EI +; POP BC +; POP DE +; POP HL +; POP AF +; OUT (RGADR),A +; LD DE,(CONT_SEC) +; +; LD A,B +; +;RAMD_TRK_CONT: +; SUB 16 +; JR C,RAMD_NO_TRK +; INC D +; JR RAMD_TRK_CONT +; +;RAMD_NO_TRK: +; ADD A,16 +; ADD A,E +; CP 16 +; JR C,RAMD_NO_TRK1 +; SUB 16 +; INC D +;RAMD_NO_TRK1: +; LD E,A +; +; LD A,H +; ADD A,B +; LD H,A +; +; LD B,0 +; +; LD (CONT_SEC),DE +; AND A +; RET +; +; + + + + INC B +WD_WR_RAMD_LOOP: + DEC B + RET Z + PUSH BC + PUSH HL + CALL RD_WR_1SEC + POP HL + CALL RAMD_NEXT_SEC + POP BC + JR WD_WR_RAMD_LOOP + +RAMD_NEXT_SEC: + PUSH HL + LD HL,CONT_SEC + LD A,16 + INC (HL) + CP (HL) + JR NZ,RAMD_NEXT_1 + LD (HL),0 + INC HL + INC (HL) +RAMD_NEXT_1: + POP HL + LD BC,0100h + ADD HL,BC + RET + +RD_WR_1SEC: + LD DE,(CONT_SEC) + CALL PAGE_CALC2 + DI + LD A,(RD_WR_COM) + AND 0C0H + OUT (RGADR),A + LD A,H + CP 0A0H + LD A,(RD_WR_COM) + LD E,0 + LD D,C + SET 6,D + JR C,RD_WR_C000 + +RD_WR_4000: + RES 7,D ; DE - адрес данных RAMD + LD C,PAGE1 + EX AF,AF' + IN A,(C) + EX AF,AF' + OUT (C),B +;****** ПЕРЕМЕЩЕНИЕ ДАННЫХ ***** + LD BC,100H + AND A + JR NZ,MOVE_NOT_EX3 + EX DE,HL +MOVE_NOT_EX3: + LDIR ; работать здесь же, в TR-DOS !!! + AND A + JR NZ,MOVE_NOT_EX4 + EX DE,HL +MOVE_NOT_EX4: +;******************************* + EX AF,AF' + OUT (PAGE1),A + EX AF,AF' +; RET_PAGE1 +MOVE_RET: + LD A,0 + OUT (RGADR),A + EI + RET + +RD_WR_C000: + SET 7,D + LD C,PAGE3 + EX AF,AF' + IN A,(C) + EX AF,AF' + OUT (C),B +;****** ПЕРЕМЕЩЕНИЕ ДАННЫХ ***** + LD BC,100H + AND A + JR NZ,MOVE_NOT_EX1 + EX DE,HL +MOVE_NOT_EX1: + LDIR ; работать здесь же, в TR-DOS !!! + AND A + JR NZ,MOVE_NOT_EX2 + EX DE,HL +MOVE_NOT_EX2: +;******************************* + EX AF,AF' + OUT (PAGE3),A + EX AF,AF' +; RET_PAGE3 + JR MOVE_RET + +;********************************************** +PAGE_CALC2: + LD A,D ; ФИЗИЧЕСКИЕ -> ЛОГИЧЕСКИЕ !!! + RRCA + RRCA + RRCA + RRCA + LD D,A + AND 0F0H + OR E + LD E,A + LD A,D + AND 0FH + LD D,A + PUSH HL + LD C,98h + CALL DISK_REDIR + SUB 4 + CALL EXP_FNS ; вычисление адреса и страницы RAM-Disk + EI + LD B,A + LD C,H + POP HL + EI + RET NC + JP ERROR_DISK + + + +;********************************************* +RAMD_X12: ; проверка диска + LD (OPER_DISK),A + CALL DISK_REDIR + CP 4 + JR C,X_AUTO_3DCE + CP 20 + RET C +; JR C,RAMD_TEST +NO_DISK2: + JP AUTO_3EE7 ; NO_DISK + +X_AUTO_3DCE: + CALL RES_1440 + LD A,(OPER_DISK) + JP AUTO_3DCE + +;************************************* +RAMD_X14: ; FORMATING + CALL get_disk_type + PUSH AF + PUSH BC + PUSH HL + CALL DISK_REDIR + CP 4 + JR C,DISK_FORM + CP 20 + JR C,RAMD_FORMAT + JP READ_ONLY + +DISK_FORM: + LD A,(MED_START+1) + CP 14H + JR Z,QVICK_FORMAT + POP HL + POP BC + POP AF + JP AUTO_1EE0 + +QVICK_FORMAT: + POP HL + POP BC + POP AF + JR Q_FORM_1 + +RAMD_FORMAT: + SUB 4 + + CALL TEST_RAMD + JP C,READ_ONLY + CALL NZ,CLEAR_RAMD + + POP HL + POP BC + POP AF + LD B,28H ; размер 640K + CALL DISK_REDIR + SUB 4 + + CALL GET_RAM_BLK ; ВЫДЕЛЕНИЕ ПАМЯТИ +; LD C,A ; КЛЮЧ RAM-Disk +Q_FORM_1: + LD HL,BUFER ; CLEAR DISK BUFER + LD DE,BUFER+1 + LD BC,0FFh + LD (HL),0 + LDIR + + LD DE,0 +RAMD_F_LOOP: + LD HL,BUFER + LD B,1 + PUSH DE + CALL WRITE_DISK + POP DE + INC E + LD A,E + CP 8 + JR NZ,RAMD_F_LOOP + LD HL,2544 + LD A,22 + JP AUTO_1F55 + +;************************************* +CLEAR_RAMD_X: + CALL RAMD_SYM + JR NC,CLEAR_RMD_A + DEC DE + LD A,(DE) + CP 13 + SCF + RET NZ +CLEAR_RAMD: + CALL DISK_REDIR +CLEAR_NEXT: + SUB 4 + RET C + CP RAMD_KEY_NUM + CCF + RET C + +CLEAR_RMD_A: + PUSH HL + PUSH DE + PUSH BC + LD C,93h ; очистка RAM-Disk + CALL EXP_FNS ; вызвать функцию + EI + POP BC + POP DE + POP HL + RET + +;********************************************************* +GET_RMD_A: +GET_RAM_BLK: ; B - ЧИСЛО ТРЕБУЕМЫХ БЛОКОВ, A - КЛЮЧ RAM-Disk + LD C,92h + CALL EXP_FNS ; A,L - КЛЮЧ RAM-Disk - номер первого блока + EI + RET NC +NO_MEM: + LD HL,NO_MEMORY + JP ERR_EXIT + +;********************************************************* +READ_ONLY: + LD HL,READ_ONLY_MSG ; Read only !! + JP ERR_EXIT + +FDD_SET: + LD A,(OPER_DISK) + LD B,A + LD C,0CCH + CALL EXP_FNS + EI + RET + +HDD_SET: + LD C,0 + CALL HD_CMD + JP C,HDD_PROG_E + + LD A,(OPER_DISK) + LD B,A + LD A,0 + LD C,0CDH + CALL EXP_FNS + EI + RET + +RAMD_SET: + CALL RAMD_SYM + RET C +RAMD_SET1: + PUSH AF + LD A,(OPER_DISK) + LD B,A + POP AF + LD C,0CBH + CALL EXP_FNS + EI + RET + +RAMD_SYM: + LD A,(DE) + INC DE + CP 20H + JR Z,RAMD_SYM + AND 223 + CP 'E' + RET C + SUB 'E' + CP 16 + CCF + RET C + PUSH AF + LD A,(DE) + CP ':' + INC DE + JR Z,RAMD_SYM_OK + DEC DE + CP 21H + JR C,RAMD_SYM_OK + POP AF + SCF + RET +RAMD_SYM_OK: + POP AF + RET + + diff --git a/ZX_EXP.ASM b/ZX_EXP.ASM new file mode 100644 index 0000000..f3c28c9 --- /dev/null +++ b/ZX_EXP.ASM @@ -0,0 +1,1743 @@ +; +; EXPANSION VER 3.00 (C) 1997 ELSY Co. +; +;******************************************************************** +; +;R0003 22-09-2001 исправление названий в меню "Hardware" +;R0002 22-09-2001 вставлен номер ПЗУ и функция биоса для него 0EDh +;R0001 22-09-2001 добавлена функция GOTO Spectrum 0FBh +; +;******************************************************************** + ORG 0 + +SPRIN2 EQU 2 ; 1 - включение Sprinter-1 первой прошивкой + ; 2 - включение Sprinter-2 первой прошивкой + + INCLUDE 'INC/SPRINT00.INC' ; константы, макросы.. + INCLUDE 'INC/CHAR_CODES.INC' +PORT_JOI EQU 0FFh + +;****************************** +; ***** BEGIN EXPANSION ***** +;****************************** + +A_0000: + JP GLOBAL_RESET + JR SET_BIOS_TO_RAM +;R0002 +ROM_NUMBER: DW 0001h + +;*************************************** + DEFS 8-$ +EXP_FNS_2_RET: + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + POP AF + JR RST_18_1 + +;*************************************** + DEFS 16-$ + JR RST10 +;*************************************** + DEFS 24-$ + JP EXP_FNS_RST18 +RST_18_1: + CALL EXP_FNS_RST18 + JR EXP_FNS_2_RET + + +; DEFS 2Bh-$ +;*************************************** + + DEFS 20h-$ + + DEFS 28h-$ + + DEFS 30h-$ + + DEFS 38h-$ +RST38: EI + RETI + +; PUSH AF +; PUSH HL +; CALL INT_PROG1 +; POP HL +; POP AF +; RET +RST10: + CALL_48 10h + RET + +;********************************** + + DEFS 66h-$ ; NMI +; RET ; резерв 3 байта для команды JP +; NOP +; NOP + RETN + NOP + +;********************************** +SET_BIOS_TO_RAM: ; программа работает на адресе 0C000h + + LD SP,0C0C0H + LD A,0E0H + LD C,PAGE3 + IN B,(C) + CALL SET_ROM_PAGES+0C000H + JP NO_SUMX_EQ + +;********************************** + +;INKEYS: +; BIT 5,(IY+1) +; RET Z +; RES 5,(IY+1) +; LD A,(05C08h) +; RET +; +;WAIT_KEY: +; CALL INKEYS +; CALL NZ,BEEP +; RET NZ +; EI +; HALT +; JR WAIT_KEY + +BEEP: + PUSH HL + PUSH DE + PUSH BC + PUSH AF + PUSH IX + + LD HL,BEEP_RET + PUSH HL + LD HL,SW_ROM + PUSH HL + LD HL,03B5h + PUSH HL + LD HL,200 + LD D,H + LD E,5 + JP SW_ROM +BEEP_RET: + + POP IX + POP AF + POP BC + POP DE + POP HL + RET + + DEFS 0A0H-$ + +TABLE_X: ; таблица для Sprinter POST-Tester-a + DB 00101000b ; "0" + DB 10111101b ; "1" + DB 00110010b ; "2" + DB 00110100b ; "3" + DB 10100101b ; "4" + DB 01100100b ; "5" + DB 01100000b ; "6" + DB 00111101b ; "7" + DB 00100000b ; "8" + DB 00100100b ; "9" + DB 00100001b ; "A" + DB 11100000b ; "B" + DB 01101010b ; "C" + DB 10110000b ; "D" + DB 01100010b ; "E" + DB 01100011b ; "F" + +; ========================== + + DEFS 0C0H-$ +;****** ВХОД ПО RANDOMIZE USR 15600 *************** + +BEGIN: ; Вход через USR 15600 + CALL CH_2 + CALL COMAND_LINE ; ВЫПОЛНЕНИЕ КОМАНДЫ, ЕСЛИ ЕСТЬ + +;****** Вход без команды. ************************* + + JP SW_ROM_1 ; ВОЗВРАТ В << MAIN MENU >> +; +CLS: +C_0D6B: ; CLS + CALL_48 0D6Bh + RET +;___________________________ + +RESTARTS EQU 0FFE0H +GLOBAL_RESET: + + DI + IM 1 + LD HL,RESTARTS + LD DE,RESTART_TXT + LD B,RESTART_TXT_L-4 +RESTART_TEST: + LD A,(DE) + CP (HL) + JR NZ,NO_RESTART + INC HL + INC DE + DJNZ RESTART_TEST + JP RESTARTS + +;********************************** +RESTART_TXT: + LD HL,RESTARTS ; 3 + LD B,16 ; 2 +REST_LOOP: + LD (HL),0 ; 2 + INC HL ; 1 + DJNZ REST_LOOP ; 2 + NOP ; 1 + NOP ; 1 + LD A,1 ; 2 + OUT (PAGE3),A ; 2 + +RESTART_TXT_L EQU $-RESTART_TXT + +;********************************** +SETUP_TXT: + + LD A,CNF_0+D_ROM16ON + OUT (CNF_PORT),A + + LD HL,1000H + LD DE,8000H + LD BC,3000H + LDIR + + LD A,CNF_0+D_ROM16OFF + OUT (CNF_PORT),A + + LD HL,08005H + + LD A,0FEh + LD BC,204Eh + OUT (C),A + + LD A,7FH + IN A,(0FEH) + CPL + AND 1 + EX AF,AF' + + LD A,0FFh + OUT (C),A + + EX AF,AF' + + JP (HL) + +SETUP_TXT_LEN EQU $-SETUP_TXT + +SYSTEM_ID_TXT: + DB "NEW_BIOS" + +NO_RESTART: + + LD SP,0BFFFH +; CALL DCP_INIT + + LD A,5 ; COM port for Printer OUT + OUT (COM_A),A + LD A,062H + OUT (COM_A),A + + LD A,0CFH ; BITS I/O + OUT (LPT1_C),A + XOR A + OUT (LPT1_C),A + OUT (LPT1_D),A ; PRINTER - PORT - all zeros + +;******* INIT SYSTEM PORTS ******************** + + LD BC,0FFEEh + XOR A + OUT (C),A ; 0 register - waits + INC C + OUT (C),A ; set 0 Waits + + DEC C + DEC A ; A->3 +; LD A,3 ; 3-nd register - boundaries + OUT (C),A + INC C + XOR A + OUT (C),A ; disable CS0,CS1 + +; no boundary set! + +; ==== POST PROCs ================================== + +POST_TEST: +; ************************************************ +; ===== Point 0 ======= +; ************************************************ + + LD A,(00A0h) + OUT (LPT1_D),A ; "0" - start + +; ==== TEST RAM BUS ======== + +TEST_RAM_BUS: + LD BC,0 + LD HL,0C000h + LD DE,00055h + +LOOP_TEST_RB: + LD (HL),E + INC L + LD (HL),D + DEC L + LD A,(HL) + XOR E + OR C + LD C,A + INC L + LD A,(HL) + XOR D + OR B + LD B,A + DEC L + + DEC E + INC D + JR NZ,LOOP_TEST_RB + LD A,E + CPL + LD E,A + INC L + INC L + BIT 4,L + JR Z,LOOP_TEST_RB + + LD A,B + OR C + JR Z,POST_RET_1 +; ************************************************ +; JP POST_RET_1 ; for test POST procedure +; ************************************************ + +ERROR_RAM_BUS: + LD HL,0A0h +ERB_2: + BIT 0,C + JR Z,ERB_1 + LD A,(HL) + AND 11011111b + OUT (LPT1_D),A + + LD DE,0 +LOOP_WTT: + DEC DE + LD A,D + OR E + JR NZ,LOOP_WTT +ERB_1: + LD A,C + RRA + RR B + RR C + + INC L + LD A,L + AND 0AFh + LD L,A + JR ERB_2 + +; ************************************************ +; ===== Point 1 ============== +; ************************************************ + +POST_RET_1: + + LD A,(0A1h) + OUT (LPT1_D),A + +; ===== TEST ADRESS BUS ====== + +TEST_ADRESS_BUS: + +; LD B,0 +; LD HL,0C001h + +; LD (HL),B +; ADD HL,HL +; LD A,H +; OR 0C0h +; LD H,A +; INC B + + LD HL,0C000h + LD DE,00000h +TSAB_1: + LD (HL),E + INC L + LD (HL),D + INC HL + INC DE + INC DE + BIT 7,H + JR NZ,TSAB_1 + + DEC HL + DEC DE + DEC DE +TSAB_2: + LD A,(HL) + CP D + JR NZ,ER_TSA + DEC HL + LD A,(HL) + CP E + JR NZ,ER_TSA + DEC HL + DEC DE + DEC DE + LD A,H + CP 0BFh + JR NZ,TSAB_2 + JR POST_RET_2 +ER_TSA: +; ************************************************ +; JP POST_RET_2 ; for test POST procedure +; ************************************************ + +TSAB_4: + LD C,D + LD B,11011111b + LD IX,TSAB_3 + JR OUT_C_BYTE +TSAB_3: + LD C,E + LD B,0FFh + LD IX,TSAB_4 + JR OUT_C_BYTE + +; ===== OUT BYTE PROGRAM ======= + +OUT_C_BYTE: + LD A,C + + RRCA + RRCA + RRCA + RRCA + + AND 0Fh + OR 0A0h + LD L,A + LD H,0 + LD A,(HL) + AND B + OUT (LPT1_D),A + + EXX + LD DE,0 +LOOP_WTT2: + DEC DE + LD A,D + OR E + JR NZ,LOOP_WTT2 + LD A,0FFh + OUT (LPT1_D),A +LOOP_WTT21: + DEC DE + LD A,D + OR E + JR NZ,LOOP_WTT21 + EXX + + LD A,C + AND 0Fh + OR 0A0h + LD L,A + LD A,(HL) + OUT (LPT1_D),A + + EXX + LD DE,0 +LOOP_WTT3: + DEC DE + LD A,D + OR E + JR NZ,LOOP_WTT3 + LD A,0FFh + OUT (LPT1_D),A +LOOP_WTT31: + DEC DE + LD A,D + OR E + JR NZ,LOOP_WTT31 + EXX + + JP (IX) + +; ************************************************ +; ===== Point 2 ========= +; ************************************************ + +POST_RET_2: + + LD A,(0A2h) + OUT (LPT1_D),A + +; ===== INIT DCP ======== + + LD HL,POST_RET_3 ; INIT PORTS + JP DCP_INIT + +; ************************************************ +; ===== Point 3 ========= +; ************************************************ + +POST_RET_3: + LD A,(0A3h) + OUT (LPT1_D),A + + IN A,(PAGE3) ; First IN command - OPEN DCP + +; ===== TEST RAM PAGES == + + IN A,(PAGE3) + EX AF,AF' + +POST_TEST_PAGES: + + LD B,0 +TSPG_1: + LD A,0FFh + LD I,A + LD A,B + OUT (PAGE3),A + LD A,0 + LD I,A + IN A,(PAGE3) + CP B + JR NZ,ERTP + DJNZ TSPG_1 + + EX AF,AF' + OUT (PAGE3),A + JR POST_RET_4 + +ERTP: + LD C,B + LD IX,ERTP_1 +ERTP_1: LD B,11011111b + JP OUT_C_BYTE + +; ************************************************ +; ===== Point 4 ========= +; ************************************************ + +POST_RET_4: + LD A,(0A4h) + OUT (LPT1_D),A + + LD B,0 +loop4test: + IN A,(0) + CP 0FFh +; JR NZ,error_bus + DJNZ loop4test + JR POST_RET_5 +error_bus: + + LD C,A +erb1: + LD IX,erbr1 + LD B,11011111b + JP OUT_C_BYTE +erbr1: + LD A,11011111b + OUT (LPT1_D),A + + LD DE,0 +LOOP_WTT4: + DEC DE + LD A,D + OR E + JR NZ,LOOP_WTT4 + JR erb1 + +; ************************************************ +; ===== Point 5 ========= +; ************************************************ + +POST_RET_5: + LD A,(0A5h) + OUT (LPT1_D),A + +;=== post end =============== + +;-( )=========================== +; LD SP,0BFFFh +; CALL EMM_FN7 ; инициализация памяти +; JP Spectrum_0 ; for TEST! +;-( )=========================== + + + IN A,(PAGE3) + EX AF,AF' ; SAVE + LD A,SYS_PAGE + OUT (PAGE3),A + + +; LD HL,SYSTEM_ID +; LD DE,SYSTEM_ID_TXT +; LD B,8 +;NEW_BIOS_L: +; LD A,(DE) +; CP (HL) +; JR NZ,NO_NEW_BIOS +; INC HL +; INC DE +; DJNZ NEW_BIOS_L + +; LD A,(HL) +; DEC HL +; LD (HL),0 ; забить "NEW_BIOS" для выхода по RESET +; OUT (PAGE3),A + +; JP 0C003H ; INIT_NEW_BIOS + +NO_SUMX_EQ: + + LD A,SYS_PAGE + OUT (PAGE3),A + +NO_NEW_BIOS: + + DB 0FDH + LD A,L + LD L,A + DB 0FDH + LD A,H + LD H,A + +; LD SP,SYS_SP +; PUSH IY +; POP HL + + LD BC,0107H + AND A + SBC HL,BC + JR NZ,NO_CONFIG_ + + LD (CONFIG_BYTE),IX + +NO_CONFIG_: + LD HL,(CONFIG_BYTE) + + EX AF,AF' + OUT (PAGE3),A + +;********************************* + + LD A,0 + OUT (PAGE3),A + OUT (PAGE0),A + LD A,5 + OUT (PAGE1),A + LD A,2 + OUT (PAGE2),A + + LD SP,0BFFFH + PUSH HL + + CALL PORTS_INIT + CALL EMM_FN7 ; инициализация памяти + + POP HL ; конфигурация + LD A,H + CP 0FFH + JR NZ,NO_SETUP_1 + LD A,L + + IF SPRIN2 EQ 2 + CP 0FDH +; LD BC,0FFFDH ; Конфигурация Sprinter-2 +PG_SP1 equ 0ECH +PG_SP2 equ 0EEH +PG_AY equ 0EAH +ELSE +; LD BC,0FFFEH ; Конфигурация Sprinter-1 + CP 0FEH + PG_AY equ 0EAH + ENDIF + JR NZ,NO_SETUP_2 +; AND A +; SBC HL,BC +; JR NZ,NO_SETUP_ + +;************************************** +; COPY BIOS ТО RAM ???? +;************************************** +; +; +; +;************************************** + +; LD A,07FH +; IN A,(0FEH) +; RRCA +; JR NC,NO_SETUP_ + +; LD A,0 +; OUT (PAGE3),A +; OUT (PAGE0),A +; LD A,5 +; OUT (PAGE1),A +; LD A,2 +; OUT (PAGE2),A + + LD A,0FFh + LD BC,204Eh ; system_port + OUT (C),A + + LD SP,0BFFFH + LD HL,L_PENTAGON + CALL SINC_DEF + + LD SP,0BFFFH + LD HL,L_PENTAGON + CALL SINC_DEF + + LD HL,SETUP_TXT + LD DE,0C000H + LD BC,SETUP_TXT_LEN + LDIR + CALL 0C000H + JP NO_SETUP_1 + +GOTO_SPEC: + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,B + LD (CONFIG_DE+1),A + EX AF,AF' + OUT (PAGE3),A + +NO_SETUP_2: +; CP 0FCH +; JR Z,SPECTRUM_0 ; Конфигурация AY + +NO_SETUP_1: + + LD A,(0A8h) + OUT (LPT1_D),A + + LD A,0FEh ; ACC-off + LD BC,204Eh + OUT (C),A + +SND_TEST_RET: + LD SP,0BFFFH + + LD HL,L_PENTAGON + CALL SINC_DEF + CALL SET_PAL_ZX + + LD HL,4104H + LD E,0 + LD B,4 + CALL LP_OPEN_S + + LD HL,5104H + LD E,0 + LD B,4 + CALL LP_OPEN_S + +;************************************* + + LD D,35H + CALL CMOS_RD + BIT 0,A + +; PUSH AF +; CALL Z,SPRINTER_1 +; POP AF +; PUSH AF +; CALL NZ,AY8910 +; POP AF + +;************************************* + +SPECTRUM_0: + XOR A ; задача 0, режим 256 килобайт +SPECTRUM_TASK: + LD IX,BASIC_128 + JP INIT_PAGES ; инициализация номеров страниц режима спектрума + +BASIC_128: +start_basic: + LD SP,0BFFFH + LD DE,5B00H ; запуск программы на BASIC-е. + LD HL,RES128_PROG + LD BC,LEN_RES128 + LDIR + DI + JP 5B00H + +;*********************************** +; Инициализация портов + +PORTS_INIT: + + LD BC,ISA_PORT + LD A,0FFH + OUT (C),A +RESETTTT: + DEC A + JR NZ,RESETTTT + OUT (C),A + + LD A,D_TBON+CNF_0 + OUT (CNF_PORT),A + LD A,D_ROM16OFF ; AROM16 = 0 + OUT (CNF_PORT),A ; включить системную страницу + + CALL DOS_OFF + + XOR A + LD BC,01FFDH + OUT (C),A ; Scorp port !!! + LD B,7FH + OUT (C),A ; 128 port !!! + + LD BC,0FFEFh ; сброс WAIT-ов !!! + LD A,0 + OUT (C),A +; +; Инициализация последовательного порта клавиатуры + + LD A,0 + OUT (COM_A),A + + LD A,1 + OUT (COM_A),A + LD A,0 ; 18H включение прерываний + OUT (COM_A),A + + LD A,3 + OUT (COM_A),A + LD A,0C1h + OUT (COM_A),A + + LD A,4 + OUT (COM_A),A + LD A,5h ; 5/7 паритет? + OUT (COM_A),A + + LD A,5 + OUT (COM_A),A + LD A,062H + OUT (COM_A),A + +; Инициализация мыши + + LD A,0 ; for MOUSE + OUT (COM_B),A + + LD A,1 + OUT (COM_B),A + LD A,0 + OUT (COM_B),A + + LD A,3 + OUT (COM_B),A + LD A,041H + OUT (COM_B),A + + LD A,4 + OUT (COM_B),A + LD A,44H + OUT (COM_B),A + + LD A,5 + OUT (COM_B),A + LD A,0E0H + OUT (COM_B),A + +; Инициализация таймера мыши + + LD A,85 ; for MOUSE + OUT (STC0_C),A + LD A,45 + OUT (STC0_C),A + +; Инициализация параллельного порта 1 (принтер) + +; LD A,0CFH ; BITS I/O +; OUT (LPT1_C),A +; XOR A +; OUT (LPT1_C),A +; OUT (LPT1_D),A ; PRINTER - PORT - all zeros + + LD A,00FH ; OUT + OUT (LPT1_C),A + OUT (LPT1_C),A + +; Инициализация параллельного порта 2 (INT/DMA ISA) + + LD BC,LPT2_C + + LD A,0CFH ; BITS I/O + OUT (C),A + LD A,03FH + OUT (C),A + LD A,0C0h + OUT (LPT2_D),A + +; Зануление ковокса + + LD A,0 + OUT (0FBH),A + +; Сброс контроллера дисковода + + LD BC,7FFDH + LD A,10H + OUT (C),A ; BASIC_48 + + CALL DOS_ON ; DOS_on + + XOR A + OUT (CBL_DIR),A + + OUT (P_DOS_FF),A + push hl + pop hl + LD A,3CH + OUT (P_DOS_FF),A + LD A,0 + OUT (WG_COM),A + + LD A,21h + OUT (0BCh),A ; set HDD1/not-HDD2 + + CALL DOS_OFF + + LD BC,7FFDH + XOR A + OUT (C),A ; BASIC_128 + + RET + +RES128_PROG: + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A ; Возврат в 128k ROM + LD C,PAGE3 + IN B,(C) + LD A,SYS_PAGE + OUT (PAGE3),A + LD DE,(CONFIG_DE) + XOR A + LD (CONFIG_DE+1),A + OUT (C),B + + LD BC,1FFDH + OUT (C),A ; Scorp порт + LD B,7FH + OUT (C),A ; 128 порт + + LD A,E ; установка конфигурации и режима TURBO + OUT (SYS_PORT_OFF),A ; CONFIG_SET + + INC D + DEC D + JP Z,0 ; 0 ВХОД В BASIC-128 + LD A,10H + OUT (C),A + DEC D + JP Z,0 ; 1 ВХОД В BASIC-48 + + LD HL,0 + PUSH HL + DEC D + JP Z,3D29H ; 2 ВХОД В TR-DOS + + XOR A + OUT (C),A + LD A,02H + LD B,1FH + OUT (C),A + DEC D + JP Z,0 ; 3 ВХОД В EXPANSION ??? + + LD A,0 + OUT (C),A + LD A,30H + LD B,7FH + OUT (C),A + DEC D + JP Z,3D29H ; 4 ВХОД В TR-DOS с закрытыми 128-ми портами + JP 0 ; 5 ВХОД В BASIC-48 с закрытыми 128-ми портами + +LEN_RES128 EQU $-RES128_PROG + +CH_2: LD A,2 +C_1601: ; OPEN CHANEL + CALL_48 #1601 + RET + +TURBO_OFF: + LD A,D_TBOFF + OUT (CNF_PORT),A + RET + +TURBO_ON: + LD A,D_TBON + OUT (CNF_PORT),A + RET + +PRINT_LINE: + LD A,(HL) + INC HL + CP 0FFh + RET Z + RST 10H + JR PRINT_LINE + +JP_HL: JP (HL) + +; JP RESET_128 + +;******************************** +; *** UTILITES *** +;******************************** +; получить раб обл +C_0030: CALL_48X 0030h + RET + +; очистить раб обл +C_19E8: CALL_48X 19E8h + RET + +; получить пространство +C_1655: CALL_48X 1655h + RET + +;**************************************** +; ******* SERVICE ******* +;**************************************** + +;******************************************************* +; ДЕШИФРАЦИЯ КОМАНДНОЙ СТРОКИ,ПЕРЕДАННОЙ ДЛЯ "EXPANSION" +;******************************************************* + +COMAND_LINE: + LD HL,(CH_ADR) +COMAND_LOOP: + LD A,(HL) + INC HL + CP ":" + JR Z,COMAND_L1 + CP 0Dh + JR NZ,COMAND_LOOP + RET + +COMAND_L1: + LD A,(HL) + CP 0EAh ; REM !!! + RET NZ + INC HL +COMAND_OK: ; ОБНАРУЖЕНА КОМАНДА ДЛЯ 'expansion' + LD A,(HL) + INC HL + CP ':' + JP Z,MENU_S1 + CP 0Dh + JP Z,MENU_S1 + CP 'I' + JR Z,COMAND_ISD + CP 'i' + JR Z,COMAND_ISD + CP 'T' + JR Z,TASK_SWITCH + CP 't' + JR Z,TASK_SWITCH + DEC HL + POP BC + JP BASIC_MENU +MENU_S1: + POP BC + CALL SERVICE + JP SW_ROM_1 + +COMAND_ISD: + LD A,(HL) + INC HL + CP 'S' + JP Z,COMAND_ISD2 + CP 's' + JP Z,COMAND_ISD2 + RET + +COMAND_ISD2: + POP BC + CALL ISDOS + JP SW_ROM_1 + + +TASK_SWITCH: + +; *** Сохранить задачу *** + PUSH IY + PUSH IX + PUSH AF + PUSH BC + PUSH DE + PUSH HL + + EXX + EX AF,AF' + + PUSH AF + PUSH BC + PUSH DE + PUSH HL + + LD A,R + PUSH AF + LD A,I + PUSH AF + + LD A,(HL) + RLCA + RLCA + RLCA + RLCA + AND 30H ; новая задача 0..3 + LD C,A + + IN A,(PAGE3) + LD B,A ; 3-я страница текущей задачи + LD A,SYS_PAGE + OUT (PAGE3),A + + LD IX,TASK_DATA ; данные задач + LD E,(IX) ; старая задача + LD D,0 + ADD IX,DE ; данные текущей задачи + LD E,16 + ADD IX,DE ; смещение данных в таблице задач + + LD (IX+1),B ; сохранить страницу 3 + LD HL,0 + ADD HL,SP + LD (IX+2),L ; сохранить стек задачи + LD (IX+3),H + SET 0,(IX) ; установить флаг сохраненности текущей задачи + RES 1,(IX) ; установить флаг, что задача покинута + + LD IX,TASK_DATA + LD E,C + LD D,0 + ADD IX,DE ; новая задача + LD E,16 + ADD IX,DE ; смещение данных в таблице задач + + BIT 1,(IX) +; JR NZ,TASK_IN_WORK ; задача в работе ??? неверное завершение + ; сбрасывать или просто возвращаться + + BIT 0,(IX) ; была сохранена/нет + JR NZ,TASK_SET ; задача существует + +; задачи не было! + LD IX,TASK_DATA + LD A,C ; задача + LD (IX),A ; установить новую задачу текущей + + JP SPECTRUM_TASK ; запустить новую задачу! + ; и пока пофиг распределение памяти! + +TASK_SET: + + LD IX,TASK_RESTORE + LD A,C + EXX + JP INIT_PAGES ; переключить все страницы! + +TASK_RESTORE: + EXX + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,C + LD IX,TASK_DATA + LD (IX),A ; установить новую задачу текущей + + LD E,A + LD D,0 + ADD IX,DE ; новая задача + LD E,16 + ADD IX,DE + + LD L,(IX+2) ; вспомнить стек + LD H,(IX+3) + LD SP,HL + LD A,(IX+1) ; вспомнить третью страницу + OUT (PAGE3),A + + + POP AF ; восстановить + LD I,A + POP AF + LD R,A + POP HL + POP DE + POP BC + POP AF + EX AF,AF' + EXX + POP HL + POP DE + POP BC + POP AF + POP IX + POP IY + ; вернуться в задачу + +NO_TASK: + POP BC + JP SW_ROM_1 + +; 259F - ADRESS BASIC128 - MAIN_MENU + + INCLUDE 'ZX_EXP/SERVICE.ASM' + +; DSS 7FDh,255 +; DB 'END' + +;************************************** + + INCLUDE 'ZX_EXP/TEST.ASM' + +; INCLUDE MAGIC.ASM + +;**************************************** + +;********************************************** + INCLUDE 'ZX_EXP/EXP_DCP2.ASM' + + INCLUDE 'ZX_EXP/FLEX.ASM' + INCLUDE 'ZX_EXP/EXP_SCR.ASM' + +; INCLUDE EXP_PIC.ASM + INCLUDE 'ZX_EXP/EXP_PIC2.ASM' + INCLUDE 'ZX_EXP/EXP_FN2.ASM' + +;********************************************** + + ;BLOCK 1400H - $,255 + +; #-5kb-############################## +; #::::::::::::::::::::::::::::::::::#\ +; #:::: Место данных для FLEX :::::::#\ +; #::::::::::::::::::::::::::::::::::#\ +; ####################################\ +; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + +;********************************************** + + BLOCK 2800H - $,255 + +; #-2kb-############################## +; #::::::::::::::::::::::::::::::::::#\ +; #:::: Здесь место для IBM-ZG ::::::#\ +; #::::::::::::::::::::::::::::::::::#\ +; ####################################\ +; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ +; INCLUDE ZGLION.ASM + INCLUDE 'ZX_EXP/FONT.ASM' + +;********************************************** + BLOCK 3000H - $,255 + INCLUDE 'ZX_EXP/EXP_FN.ASM' + + INCLUDE 'ZX_EXP/EXP_LP2.ASM' +; INCLUDE EXP_LP3.ASM + +;********************************************** +FN_SENT_BYTE: + LD E,A ; сохранить байт + CALL SENT_POLU_BYTE ; передать полубайт из Е + RET C ; возврат по ошибке + LD A,E ; сдвинуть байт на 4 бита + RRCA + RRCA + RRCA + RRCA + LD E,A + +SENT_POLU_BYTE: ; передать половину байта _E_!! + LD A,E + OR 0F0h ; установить старшие биты LPT дата + OUT (LPT1_D),A + LD BC,0 ; счетчик тайм-аута + +WAIT_SENT_1: + IN A,(PORT_JOI) ; проверить бит 4 на 1 при готовности PC + BIT 4,A + JR NZ,CONTINUE_SENT + DEC BC + LD A,B + OR C + JR NZ,WAIT_SENT_1 + XOR A + SCF ; тайм-аут + RET + +CONTINUE_SENT: ; PC - готов + LD A,E + AND 0Fh ; сбросить старшие биты LPT - + OUT (LPT1_D),A + + LD BC,0 ; счетчик тайм-аута + +WAIT_SENT_2: + IN A,(PORT_JOI) ; проверить бит 4 на 0 - сообщение от PC + BIT 4,A + JR Z,CONTINUE_SENT2 + DEC BC + LD A,B + OR C + JR NZ,WAIT_SENT_2 + LD A,E + OR 0F0h ; установить старшие биты LPT дата + OUT (LPT1_D),A + XOR A + SCF ; тайм-аут + RET + +CONTINUE_SENT2: ; полубайт передан + LD A,E + OR 0F0h ; установить старшие биты LPT дата + OUT (LPT1_D),A + XOR A + RET + + +FN_RESEIVE_B: ; принять байт в A + + CALL RESEIVE_POLU_BYTE ; принять полубайт + RET C ; возврат по ошибке + RLCA + RLCA + RLCA + RLCA + AND 0F0h + LD E,A + CALL RESEIVE_POLU_BYTE ; второй + RET C + AND 0Fh + OR E + RET ; байт принят + +RESEIVE_POLU_BYTE: ; принять половину байта в Е + + LD A,0F0h ; установить старшие биты LPT дата + OUT (LPT1_D),A + LD BC,0 ; счетчик тайм-аута + +WAIT_RES_1: + IN A,(PORT_JOI) ; проверить бит 4 на 1 при готовности PC + BIT 4,A + JR NZ,CONTINUE_RES + DEC BC + LD A,B + OR C + JR NZ,WAIT_RES_1 + XOR A + SCF ; тайм-аут + RET + +CONTINUE_RES: ; PC - готов, ждать 0 + XOR A ; сбросить старшие биты LPT - ждем полубайт + OUT (LPT1_D),A + + LD BC,0 ; счетчик тайм-аута + +WAIT_RES_2: + IN A,(PORT_JOI) ; проверить бит 4 на 0 - сообщение от PC + BIT 4,A + JR Z,CONTINUE_RES2 + DEC BC + LD A,B + OR C + JR NZ,WAIT_RES_2 + LD A,0F0h ; установить старшие биты LPT дата + OUT (LPT1_D),A + SCF + RET ; ошибка + +CONTINUE_RES2: ; полубайт выставлен +LOOP_EQ: ; прочитать еще раз, что бы совпало + AND 0Fh + LD B,A + IN A,(PORT_JOI) + AND 0Fh + CP B + JR NZ,LOOP_EQ + + OR 0F0h ; установить старшие биты LPT дата в 1 - принято + OUT (LPT1_D),A + RET + +;********************************************** +; DSS 3C10h,255 +;KBD_RET: +; LD A,D_ROM16OFF +; OUT (SYS_PORT_OFF),A +; JR KBD_EXP +; NOP +; NOP +;KBD_RET2: +; LD A,D_ROM16OFF +; OUT (SYS_PORT_OFF),A +; JR KBD_OFF1 +; NOP +; NOP +;KBD_EXP: +; CALL RET_INS +; PUSH HL +; CALL INT_PROG1 +; POP HL +; CALL DOS_ON +; JR KBD_RET +;KBD_OFF1: +; CALL RET_INS +; CALL KBD_OFF +; CALL DOS_ON +; JR KBD_RET2 + +;****************************************************** + + BLOCK 3CC0h - $,255 +MAGIC_1: ; ЗАГЛУШКА ДЛЯ MAGIC + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + POP AF + JP MAGIC_1 +MAGIC_3: + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + POP AF + RETN +MAGIC_2: +; CALL MG_BEGIN + JR MAGIC_3 + + +;************************* + BLOCK #3CE0-$,0 +SW_ROM_1: + LD HL,#259F + PUSH HL + LD HL,5B00h + LD A,(HL) + CP #F5 ; #F5 - опкод 'PUSH AF'. Проверяется то ли в #5B00 + JR Z,JP_HL_48 ; ВОЗВРАТ К МЕНЮ BASIC128 + POP HL + JR SW_ROM ; ПРОСТОЙ ВОЗВРАТ + +; DEFS 3CF0h-$ +;JMP_48: +; LD HL,00h +; JR JP_HL_48 +; +; NOP +; NOP +; NOP + + DEFS 3CF8H-$ +JP_HL_48: + PUSH HL +SW_ROM: + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + POP AF + RET +;****************************************** + BLOCK 3D00H - $,255 +DOS_ON: + NOP + RET + +;****************************************** + BLOCK 3D13H - $,255 + NOP + JP 18H + +;****************************************** + BLOCK 3E00H - $,255 + +DOS_OFF: + PUSH AF + PUSH BC + LD BC,(5BFFH) + LD A,0C9H + LD (5BFFh),A + CALL 5BFFh + LD (5BFFh),BC + POP BC + POP AF + RET + + +START_HDD: + + LD C,2 + CALL EXP_HDD + RET C + + LD A,0 + OUT (0FEH),A + + LD HL,4000H + LD DE,4001H + LD BC,1AFFH + LD (HL),0 + LDIR + + LD HL,0 + LD IX,LP_SCR_80 + CALL WIN_OPEN_WIN + + LD HL,2050H + LD DE,0 + LD B,07H + CALL LP_CLS_WIN + + LD DE,0 + CALL LP_SET_PLACE + + LD HL,ID_SPRINTER + LD E,47H + LD B,L_ID + CALL LP_PRINT_LINE + +; LD C,80H +; LD B,0 +; LD E,0 +; CALL LP_OPEN_S + + +; LD DE,0 +; LD C,84H +; CALL LP_SET_PLACE + +; LD HL,HDD_MSG +; LD B,HDD_MSG_LEN +; LD E,47H +; CALL LP_PRINT_LINE + + LD C,0 + CALL EXP_HDD + RET C + + LD DE,1 + LD IX,0 + LD B,16 + LD HL,8000H + LD C,5 + CALL EXP_HDD + RET C + LD HL,8000H + LD DE,HDD_MSG + LD B,HDD_MSG_LEN +START_HDD_L: + LD A,(DE) + CP (HL) + JR NZ,NO_BOOT + INC HL + INC DE + DJNZ START_HDD_L + JP (HL) +NO_BOOT: + RET + + +HDD_MSG: DB "Starting...",0 + +HDD_MSG_LEN EQU $-HDD_MSG + +;HDD_MSG_NO: DB "Boot record not found" +;HDD_NO_LEN EQU $-HDD_MSG_NO + +SAVE_AUTOSTART: + LD HL,8000H + LD DE,HDD_MSG + LD B,HDD_MSG_LEN +SAVE_AUTO_L1: + LD A,(DE) + CP (HL) + RET NZ + INC HL + INC DE + DJNZ SAVE_AUTO_L1 + + LD C,0 + CALL EXP_HDD + RET C + + LD HL,8000H + LD A,0 + LD DE,1 + LD IX,0 + LD B,16 + LD C,6 + CALL EXP_HDD + RET + +;****************************************** + BLOCK 3F00H - $,255 + +FN_LIB: + SCF + RET + + IN A,(PAGE3) + LD B,A + LD A,SYS_PAGE + OUT (PAGE3),A + PUSH HL + LD L,C + LD H,0C1H + LD A,(HL) + POP HL + AND A + SCF + JR Z,FN_LIB_RET + OUT (PAGE3),A + LD (0C0FEH),SP + LD SP,0C0F0H + PUSH BC + CALL 0C100H + POP BC + LD SP,(0C0FEH) + LD A,B + OUT (PAGE3),A + RET + +FN_LIB_RET: + LD A,B + OUT (PAGE3),A + SCF + RET + + +FN_KBD_OUT: ; вход A - byte + ; B - speed_parameter + + AND A + LD E,A + LD D,0FFh + JP PE,kbd_parity ; установка паритета PE/PO ??? + LD D,0FEh +kbd_parity: + AND A ; первый бит - start-bit - 0 + RL E + RL D ; в DE - 11 бит для передачи + LD C,11 + +kbd_loop: + LD A,5 + OUT (COM_A),A + LD A,60h + BIT 0,E + JR NZ,no_inv + XOR 2 ; данные +no_inv: + LD L,A + OUT (COM_A),A ; выставить данные + + LD A,5 + OUT (COM_A),A + LD A,L + OR 80h ; синхроимпульс + OUT (COM_A),A ; импульс синхро + LD A,B ; speed_par +kbd_loop1: + PUSH HL + POP HL + DEC A + JR NZ,kbd_loop1 + LD A,5 + OUT (COM_A),A + LD A,L + OUT (COM_A),A ; снять импульс синхро + + LD A,B ; speed_par +kbd_loop2: + PUSH HL + POP HL + DEC A + JR NZ,kbd_loop2 + RR D + RR E + DEC C + JR NZ,kbd_loop + + LD A,5 + OUT (COM_A),A + LD A,60h + OUT (COM_A),A ; закончить передачу + AND A + RET + + + BLOCK 3FD0H - $,0 +FN1_RET: + PUSH AF + LD A,D_ROM16ON + OUT (CNF_PORT),A + POP AF + RST 18H + JR FN1_RET + +;****************************************** + BLOCK 3FE0H - $,255 + +SOUND_TEST: + LD A,D_ROM16ON ; AROM16 = 1 + OUT (CNF_PORT),A + JP SND_TEST_RET + + BLOCK 3FE8H - $,255 +HDD_EXE: +EXP_HDD: + PUSH AF + LD A,D_ROM16ON + OUT (CNF_PORT),A + POP AF + RET + + BLOCK 3FF0H-$,255 + + BLOCK 3FF8H-$,255 +EXP_FNS_RET: + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + JP EXP_FNS + +;****************************************** + BLOCK 4000h-$,255 +; + diff --git a/ZX_EXP/EXP_DCP2.ASM b/ZX_EXP/EXP_DCP2.ASM new file mode 100644 index 0000000..23ed610 --- /dev/null +++ b/ZX_EXP/EXP_DCP2.ASM @@ -0,0 +1,452 @@ +DCP_INIT: + EXX + LD HL,ALTERA + LD DE,0C000H +DCP_L0: + LD B,8 + LD C,(HL) + INC HL +DCP_L1: + RLC C + JR C,DCP_L2 + LD A,0 + LD (DE),A + INC DE + DJNZ DCP_L1 + JR DCP_L3 +DCP_L2: + LD A,(HL) + INC HL + LD (DE),A + INC DE + DJNZ DCP_L1 +DCP_L3: + inc d + dec d + JR NZ,DCP_L0 + IN A,(PAGE3) + EXX + JP (HL) +; RET + +;*********************************** + +;DCP_INIT_new: +; DB 0DDH +; LD C,L +;; LD C,A +; DB 0DDH +; LD B,H +;; LD B,A +; DB 0FDH +; LD E,L +;; LD E,A +; DB 0FDH +; LD D,H +;; LD D,A +; EXX +; +; LD HL,0C000h +; LD DE,0C001h +; LD BC,3FFFH +; LD (HL),L +; LDIR +; +; LD IY,DCP_DATA_NEW +; LD IX,DCP_INN_RET +; +;DCP_INN_LOOP: +; LD L,(IY) +; LD H,(IY+1) +; LD E,(IY+2) +; LD D,(IY+3) +; LD B,(IY+4) +; JR DCP_FN0M +; +;DCP_INN_RET: +; LD BC,5 +; ADD IY,BC +; LD A,(IY+2) +; OR (IY+3) +; JR NZ,DCP_INN_LOOP +; +; EXX +;; LD A,C +; DB 0DDH +; LD L,C +;; LD B,A +; DB 0DDH +; LD H,B +;; LD A,E +; DB 0FDH +; LD L,E +;; LD A,D +; DB 0FDH +; LD H,D +; JP (HL) +;; RET +; +; + +; Функция дешифратора портов. +; HL - адрес +; DE - маска - 0 изменяемые биты, 1 неизменяемые +; B - порт + +DCP_FN0M: + + LD A,L + AND E + LD L,A + + LD A,H + AND D +; AND 3FH + OR 0C0H + LD H,A + + LD A,D + OR 0C0H + LD D,A + +DCP_FN0_L1: + LD (HL),B + + LD A,L ; замаскировать неизменяемые биты 1-ми + OR E ; для прохождения переноса + INC A ; увеличить адрес + JR Z,DCP_FN0_L2 ; возник перенос + + OR E + XOR E ; обнулить неизменяемые биты + LD C,A ; изменяемая часть + + LD A,L + AND E ; выделить неизменяемую + OR C + LD L,A ; добавить изменяемую часть + + JR DCP_FN0_L1 ; цикл + +DCP_FN0_L2: ; A = 0 +; OR E +; XOR E +; LD C,A + LD A,L ; забить изменяемые биты нулями + AND E +; OR C + LD L,A + + LD A,H ; замаскировать неизменяемые биты 1-ми + OR D ; для прохождения переноса + INC A ; увеличить адрес + JR Z,DCP_FN0_L3 + + OR D + XOR D + LD C,A ; изменяемая часть + + LD A,H + AND D ; выделить неизменяемую + OR C + LD H,A ; добавить изменяемую часть + JR DCP_FN0_L1 + +DCP_FN0_L3: + JP (IX) + + +;DCP_DATA_NEW: +;; DATA FOR DCP +;; CCED/AAAAAAAAA DATA +;; NN1OW116517210 +;; FF2SR54 3 +;; 108 +;; +;; Border ....0..11.1110 0C2H +; DW 00000001101110B +; DW 00001001101111B +; DB 0C2H +;; Keyboard ....1..11.1110 040H +; DW 00001001101110B +; DW 00001001101111B +; DB 040H +;; Kempston ...11..00.0111 015H +; DW 00011000000111B +; DW 00011001101111B +; DB 015H +;; +;; DRQ/INTRQ ...01..11.1111 015H +; DW 00001001101111B +; DW 00011001101111B +; DB 015H +;; WR_PDOS ...00..11.1111 014H +; DW 00000001101111B +; DW 00011001101111B +; DB 014H +;; CS_WG93_1F ...0...00.0111 010H +; DW 00000000000111B +; DW 00010001101111B +; DB 010H +;; CS_WG93_3F ...0...01.0111 011H +; DW 00000000100111B +; DW 00010001101111B +; DB 011H +;; CS_WG93_5F ...0...10.0111 012H +; DW 00000001000111B +; DW 00010001101111B +; DB 012H +;; CS_WG93_7F ...0...11.0111 013H +; DW 00000001100111B +; DW 00010001101111B +; DB 013H +;; +;; CNF_PORT ........1.0100 0C6H +; DW 00000000100100B +; DW 00000000101111B +; DB 0C6H +;; +;; SOUND_BFFD_WR ....0101111101 091H +; DW 00000101111101B +; DW 00001111111111B +; DB 091H +;; SOUND_FFFD_WR ....0111111101 090H +; DW 00000111111101B +; DW 00001111111111B +; DB 090H +;; SOUND_FFFD_RD ....11.1111101 000H +; DW 00001101111101B +; DW 00001101111111B +; DB 0 +;; +;; COVOX_4F ...10.....0111 088H +; DW 00010000000111B +; DW 00011000001111B +; DB 088H +;; COVOX_FB ...10..11.1011 088H +; DW 00010001101011B +; DW 00011001101111B +; DB 088H +;; CASH__FB ...11..11..011 088H +; DW 00011001100011B +; DW 00011001100111B +; DB 088H +;; CASH__80 ...11..00.1000 088H +; DW 00011000001000B +; DW 00011001101111B +; DW 088H +;; +;; P_XTR ...1...01.1.11 032H +; DW 00010000101011B +; DW 00010001101011B +; DB 032H +;; +;; SC_256_1FFDH 0....001101101 0C0H +; DW 00000001101101B +; DW 10000111111111B +; DB 0C0H +;; ZX_128_7FFDH 0.0..0111.1101 0C1H +; DW 00000011101101B +; DW 10100111101111B +; DB 0C1H +;; ZX_128_7FFDH 0..0.0111.1101 0C1H +; DW 00000011101101B +; DW 10010111101111B +; DB 0C1H +; +;; ZX_128_7FFDH 100..0.11.1101 0C1H +; DW 10000001101101B +; DW 11100101101111B +; DB 0C1H +;; ZX_128_7FFDH 10.0.0.11.1101 0C1H +; DW 10000001101101B +; DW 11010101101111B +; DB 0C1H +; +;; SC_256_1FFDH 11...001101101 0C0H +; DW 11000001101101B +; DW 11000111111111B +; DB 0C0H +;; ZX_128_7FFDH 110..0111.1101 0C1H +; DW 11000011101101B +; DW 11100111101111B +; DB 0C1H +;; ZX_128_7FFDH 11.0.0111.1101 0C1H +; DW 11000011101101B +; DW 11010111101111B +; DB 0C1H +;; +;; HDD_DATA ...0...10.0000 020H +; DW 00000001000000B +; DW 00010001101111B +; DB 020H +;; HDD_1F1 ...0.0010.0001 021H +; DW 00000001000001B +; DW 00010111101111B +; DB 021H +;; HDD_1F2 ...0.0010.0010 022H +; DW 00000001000010B +; DW 00010111101111B +; DB 022H +;; HDD_1F3 ...0.0010.0011 023H +; DW 00000001000011B +; DW 00010111101111B +; DB 023H +;; HDD_1F4 ...0.0010.0100 024H +; DW 00000001000100B +; DW 00010111101111B +; DB 024H +;; HDD_1F5 ...0.0010.0101 025H +; DW 00000001000101B +; DW 00010111101111B +; DB 025H +;; HDD_1F6 ...0.0110.0010 026H +; DW 00000011000010B +; DW 00010111101111B +; DB 026H +;; HDD_1F7 ...0.0110.0011 027H +; DW 00000011000011B +; DW 00010111101111B +; DB 027H +;; HDD_3F6 ...0.0110.0100 028H +; DW 00000011000100B +; DW 00010111101111B +; DB 028H +;; HDD_3F7 ...0.0110.0101 029H +; DW 00000011000101B +; DW 00010111101111B +; DB 029H +;; +;; HDD_DATA 00.....10.0000 020H +; DW 00000001000000B +; DW 11000001101111B +; DB 020H +;; HDD_1F1 00...0010.0001 021H +; DW 00000001000001B +; DW 11000111101111B +; DB 021H +;; HDD_1F2 00...0010.0010 022H +; DW 00000001000010B +; DW 11000111101111B +; DB 022H +;; HDD_1F3 00...0010.0011 023H +; DW 00000001000011B +; DW 11000111101111B +; DB 023H +;; HDD_1F4 00...0010.0100 024H +; DW 00000001000100B +; DW 11000111101111B +; DB 024H +;; HDD_1F5 00...0010.0101 025H +; DW 00000001000101B +; DW 11000111101111B +; DB 025H +;; HDD_1F6 00...0110.0010 026H +; DW 00000011000010B +; DW 11000111101111B +; DB 026H +;; HDD_1F7 00...0110.0011 027H +; DW 00000011000011B +; DW 11000111101111B +; DB 027H +;; HDD_3F6 00...0110.0100 028H +; DW 00000011000100B +; DW 11000111101111B +; DB 028H +;; HDD_3F7 00...0110.0101 029H +; DW 00000011000101B +; DW 11000111101111B +; DB 029H +;; +;; PAGE0 ...0...00.1010 0E8H +; DW 00000000001010B +; DW 00010001101111B +; DB 0E8H +;; PAGE1 ...0...01.1010 0E9H +; DW 00000000101010B +; DW 00010001101111B +; DB 0E9H +;; PAGE2 ...0...10.1010 0EAH +; DW 00000001001010B +; DW 00010001101111B +; DB 0EAH +;; PAGE3 ...0...11.1010 0F0H +; DW 00000001101010B +; DW 00010001101111B +; DB 0F0H +;; +;; PAGE0 00.....00.1010 0E8H +; DW 00000000001010B +; DW 11000001101111B +; DB 0E8H +;; PAGE1 00.....01.1010 0E9H +; DW 00000000101010B +; DW 11000001101111B +; DB 0E9H +;; PAGE2 00.....10.1010 0EAH +; DW 00000001001010B +; DW 11000001101111B +; DB 0EAH +;; PAGE3 00.....11.1010 0F0H +; DW 00000001101010B +; DW 11000001101111B +; DB 0F0H +;; +;; Y_PORT(RGADR) ...0...00.1001 0C4H +; DW 00000000001001B +; DW 00010001101111B +; DB 0C4H +;; RGMOD ...0...10.1001 0C5H +; DW 00000001001001B +; DW 00010001101111B +; DB 0C5H +;; +;; Y_PORT(RGADR) 00.....00.1001 0C4H +; DW 00000000001001B +; DW 11000001101111B +; DB 0C4H +;; RGMOD 00.....10.1001 0C5H +; DW 00000001001001B +; DW 11000001101111B +; DB 0C5H +;; +;; ISA_REG ...00100101101 01BH +; DW 00000100101101B +; DW 00011111111111B +; DB 01BH +;; CMOS_DWR ...001.0111101 01EH +; DW 00000100111101B +; DW 00011101111111B +; DB 01EH +;; CMOS_AWR ...00110101101 01DH +; DW 00000110101101B +; DW 00011111111111B +; DB 01DH +;; CMOS_DRD ...011.0111101 01CH +; DW 00001100111101B +; DW 00011101111111B +; DB 01CH +;; +;; ISA_REG 00..0100101101 01BH +; DW 00000100101101B +; DW 11001111111111B +; DB 01BH +;; CMOS_DWR 00..01.0111101 01EH +; DW 00000100111101B +; DW 11001101111111B +; DB 01EH +;; CMOS_AWR 00..0110101101 01DH +; DW 00000110101101B +; DW 11001111111111B +; DB 01DH +;; CMOS_DRD 00..11.0111101 01CH +; DW 00001100111101B +; DW 11001101111111B +; DB 01CH +; +; DW 0,0,0 ; DCP END MARKER +; +; +; +; +; diff --git a/ZX_EXP/EXP_FN.ASM b/ZX_EXP/EXP_FN.ASM new file mode 100644 index 0000000..b787369 --- /dev/null +++ b/ZX_EXP/EXP_FN.ASM @@ -0,0 +1,764 @@ +TAB_FNS: +;FN_80h + DW LP_OPEN_S ; открытие окна + DW LP_PRINT_ALL ; печать символа в окно + DW LP_PRINT_SYM ; печать символа без атр + DW LP_PRINT_ATR ; печать только атрибута + DW LP_SET_PLACE ; установка позиции печати + DW LP_PRINT_LINE ; печать строки длиной B + DW LP_PRINT_LINE2 ; печать строки -//- без атрибутов + DW LP_PRINT_LINE3 ; печать строки длиной B до D +;FN_88h + DW LP_PRINT_LINE4 ; печать строки -//- без атрибутов + DW LP_CLS_WIN + DW LP_SCROLL_UD + DW LP_PRINT_LINE5 + DW LP_PRINT_LINE6 + DW LP_CLS_WIN_2 + DW LP_GET_PLACE + DW FN_TURBO +;FN_90h + DW EMM_FN0 ; неразрушающее определение объема ОЗУ. + DW EMM_FN1 ; инициализация распределения памяти + DW EMM_FN2 ; получить блок памяти + DW EMM_FN3 ; освободить блок памяти + DW EMM_FN4 ; получить номерa страниц RAM-Disk + DW EMM_FN5 ; получить следующую страницу + DW EMM_FN6 ; получить адреса портов + DW EMM_FN7 ; инициализация *** +;FN_98h + DW RAMD_CALC_PAGE ; Fn 98h + DW SET_DISK_TYPE + DW DISK_REDIR + DW FN_NO + DW FN_NO + DW EMM_DIV ; разделения блока на два. + DW EMM_ADD ; слияние двух блоков + DW MSDOS_COLD_VARS ; инициализация всей памяти +;FN_A0h + DW PIC_FN0 ; ОТКРЫТИЕ ОКНА - Fn 0A0h + DW PIC_FN1 ; ВЫВЕСТИ ТОЧКУ + DW PIC_FN2 ; ВЫВОД ЛИНИИ COPY + DW PIC_FN3 ; ВЫВОД ЛИНИИ FILL + DW PIC_FN4 ; ВЫВОД ПАЛИТРЫ + DW PIC_FN5 ; УСТАНОВКА RGMOD + DW PIC_FN6 ; A - page_pal, E - номер палитры, B - тип палитры + DW PIC_FN7 +;FN_A8h + DW PIC_FN8 + DW PIC_FN9 + DW PIC_FN10 + DW PIC_FN11 + DW PIC_FN12 + DW PIC_FN14 + DW PIC_FN14 + DW PIC_FN15 +;FN_B0h + DW WIN_OPEN_WIN ; открытие окна по описателю + DW WIN_CLOSE_WIN ; закрытие окна + DW WIN_COPY_WIN ; сохранение текстового окна в памяти + DW WIN_REST_WIN ; восстановление текстового окна из памяти + DW WIN_GET_SYM ; взять символ + DW WIN_PUT_SYM ; положить символ + DW WIN_SET_ZG ; загрузка знакогенератора + DW WIN_MOVE_WIN ; переместить окно +;FN_B8h + DW WIN_GET_ZG ; получить знакогенератор + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO +;FN_C0h + DW EMM_FN0 ; получить данные об объеме памяти и кол-во своб. стр. + DW EMM_FN1 ; инициализация распределения памяти + DW EMM_FN2M ; получить блок памяти + DW EMM_FN3M ; освободить блок памяти + DW EMM_FN4M ; получить номер страницы в блоке памяти + DW EMM_FN5M ; получить список страниц блока памяти + DW EMM_FN6 ; получить адреса портов окон + DW EMM_FN5 ; получить следующую страницу блока +;FN_C8h + DW BLK_R_W ; функция чтения/записи в блок памяти + DW BLK_TO_RAMD ; назначить блок RAM-Disk-у + DW RAMD_CLEAR ; освободить RAM-Disk + DW RAMD_TO_DRV ; назначить RAM-Disk на дисковод + DW FDD_TO_DRV ; назначить REAL_DRIVE на дисковод + DW HDD_TO_DRV ; назначить HDD на дисковод + DW GET_RAMD_ST ; получить тип назначения на RAM-Disk + DW GET_DRV_ST ; получить тип назначения на дисковод +;FN_D0h + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB +;FN_D8h + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB + DW FN_LIB +;FN_E0h + DW LP_PRINT_LINE_DIR + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO + DW FN_NO +;FN_E8h + DW FN_SENT_BYTE ; послать байт через PC_link + DW FN_RESEIVE_B ; принять байт через PC_link + DW FN_KBD_OUT ; послать байт в клавиатуру + DW FN_NO + DW FN_NO + DW FN_CRIPT + DW AY8910 + DW FN_VERSION +;FN_F0h + DW SPRINTER_1 + DW SPRINTER_2 + DW FN_SINC + DW SPRINTER_ALL + DW DCP_FN0 + DW CMOS_TEST + DW CMOS_RD + DW CMOS_WR +;FN_F8h + DW SET_ROM_PAGES + DW READ_PORTS ; + DW WRITE_PORTS ; + DW GOTO_SPEC ; Goto Spectrum! + DW FN_NO + DW FN_RESET + DW FN_NO ; SAVE_AUTOSTART + DW FN_VERSION + +EXP_FNS: ; Вход в функцию из DOS + POP AF + CALL EXP_FNS1 + CALL DOS_ON + JP EXP_FNS_RET + +;************************************ +EXP_FNS_RST18: ; Вход в функцию по RST18 и RST8 + bit 7,c + JR Z,ower2_fn + CALL EXP_FNS1 + RET +ower2_fn: + BIT 6,C + RES 6,C + JR NZ,EXP_HDD_X + SCF + RET +EXP_HDD_X: + CALL EXP_HDD + RET +;********************************** + +FN_CRIPT: + DEC B + SCF + RET NZ + LD HL,(ROM_NUMBER) + LD A,(ROM_NUMBER+2) + LD BC,5281h + LD DE,47E8h + AND A + RET + +;********************************** + +START_DI: + PUSH AF + LD A,R + JP PE,XX_DI + LD A,R +XX_DI: LD A,80h + JP PE,XX_DI2 + XOR A +XX_DI2: LD R,A + DI + POP AF + RET + +END_DI: PUSH AF + LD A,R + BIT 7,A + JR Z,XX_DI3 + EI + POP AF ; PE + RET + +XX_DI3: DI + POP AF ; PO + RET + +;********************************** +EXP_FNS1: + PUSH HL + PUSH AF + LD L,C + SLA L + LD H,TAB_FNS/256 + LD A,(HL) + INC L + LD H,(HL) + LD L,A + POP AF + EX (SP),HL + RET + +READ_PORTS: +WRITE_PORTS: +FN_NO: + SCF + RET + +SET_ROM_PAGES: ; in A - page, B - new ROM-page + ; out B - old ROM-page + EX AF,AF' + LD A,CNF_0 + OUT (CNF_PORT),A + LD C,PAGE2 ; получить страницу + IN D,(C) + LD A,40H ; установить новую + OUT (C),A + LD A,(08000H) ; сохранить то что было + LD L,A + LD A,(08200H) + LD H,A + EX AF,AF' ; страница + LD (08000H),A ; установить порт ROM TR-DOS + LD (08200H),A + EX AF,AF' + LD A,B + LD BC,0 + EX AF,AF' + IN A,(C) + EX AF,AF' + OUT (C),A ; установить новый TR-DOS + EX AF,AF' + LD B,A + LD A,L + LD (08000H),A ; вернуть порт + LD A,H + LD (08200H),A ; вернуть порт + LD C,PAGE2 + LD A,SYS_PAGE + OUT (C),A + LD A,(CONFIG_DE-4000H) + OUT (C),D ; вернуть страницу + OUT (CNF_PORT),A + AND A + RET + +;TAB_SIZE EQU $-TAB_FNS + +;****************************************** + +;********************************************* +;***************************************** +; +; Функция дешифратора портов. +; HL - адрес +; DE - маска - 0 изменяемые биты, 1 неизменяемые +; B - порт +; +; ALL STACKS, DI +; +;***************************************** +DCP_FN0: + AND A + JP Z,PORTS_INIT + + PUSH IX + LD IX,RET_DCP_FN0 + + IN A,(PAGE3) + EX AF,AF' + LD A,DCP_PAGE + OUT (PAGE3),A + + JP DCP_FN0M + +RET_DCP_FN0: + + EX AF,AF' + OUT (PAGE3),A + AND A + POP IX + RET + + +BLK_TO_RAMD: ; RAM-Disk A, BLK - B + CP RAMD_KEY_NUM + CCF + RET C + + PUSH HL + PUSH BC + LD L,A + + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,L + LD HL,RAMD_KEYS-4000H + ADD A,L + LD L,A + LD A,(HL) ; ключ блока + AND A + JR NZ,BLK_BUSY ; RAM-Disk занят - ошибка + LD (HL),B + LD A,C + OUT (PAGE2),A + + LD A,B + AND A + POP BC + POP HL + RET + +BLK_BUSY: + LD A,C + OUT (PAGE2),A + + SCF + POP HL + RET + + +RAMD_CLEAR: ; RAM-Disk A + CP RAMD_KEY_NUM + CCF + RET C + + PUSH HL + LD L,A + + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,L + LD HL,RAMD_KEYS-4000H ; RAM-Disk свободем + ADD A,L + LD L,A + LD B,A ; запомнить удаляемый рамдиск + LD A,(HL) + AND A + JR Z,BLK_BUSY ; возврат с ошибкой + LD (HL),0 + LD A,C + OUT (PAGE2),A + + AND A + POP HL + RET + + +RAMD_TO_DRV: ; RAM-Disk A, DRV - B + CP RAMD_KEY_NUM + CCF + RET C + LD C,A + LD A,B + CP 4 + CCF + RET C + + LD HL,DISK_TYPE-4000H + LD L,B + + IN A,(PAGE2) + LD B,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,C + ADD A,4 + LD (HL),A + LD A,B + OUT (PAGE2),A + AND A + RET + +FDD_TO_DRV: ; Disk A, DRV - B + CP 4 + CCF + RET C + LD C,A + + LD A,B + CP 4 + CCF + RET C + + LD HL,DISK_TYPE-4000H + LD L,B + + IN A,(PAGE2) + LD B,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD (HL),C + LD A,B + OUT (PAGE2),A + + AND A + RET + + +HDD_TO_DRV: ; HDD A, DRV - B + AND 0FH + LD C,A + LD A,B + CP 4 + CCF + RET C + + LD HL,DISK_TYPE-4000H + LD L,B + + IN A,(PAGE2) + LD B,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,C + ADD A,40H + LD (HL),A + LD A,B + OUT (PAGE2),A + + AND A + RET + +GET_RAMD_ST: + CP RAMD_KEY_NUM + CCF + RET C + + PUSH BC + + LD HL,RAMD_KEYS-4000H + ADD A,L + LD L,A + + IN A,(PAGE2) + LD B,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD C,(HL) + LD A,B + OUT (PAGE2),A + + LD A,C + + POP BC + + AND A + RET + +GET_DRV_ST: + CP 4 + CCF + RET C + + PUSH BC + + LD HL,DISK_TYPE-4000H + ADD A,L + LD L,A + + IN A,(PAGE2) + LD B,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD C,(HL) + LD A,B + OUT (PAGE2),A + + LD A,C + + POP BC + + AND A + RET + + +CMOS_EMU_WR: + PUSH DE + + LD C,PAGE3 + IN B,(C) + LD E,SYS_PAGE + OUT (C),E + LD E,D + LD D,0FFH + LD (DE),A + OUT (C),B + + POP DE + RET + +;*********************************************** +; +; CMOS - 35h,36h - установки бейсика +; +;*********************************************** + +CMOS_WR: +WR_CMOS: ; запись с CMOS + CALL CMOS_TEST + JR C,CMOS_EMU_WR +XWR_CMOS: ; запись с CMOS + LD BC,CMOS_AWR + OUT (C),D + LD BC,CMOS_DWR + OUT (C),A + RET + + +CMOS_EMU_RD: + PUSH DE + + LD C,PAGE3 + IN B,(C) + LD E,SYS_PAGE + OUT (C),E + LD E,D + LD D,0FFH + LD A,(DE) + OUT (C),B + + POP DE + RET + +CMOS_RD: +RD_CMOS: ; чтение из CMOS + CALL CMOS_TEST + JR C,CMOS_EMU_RD +XRD_CMOS: ; чтение из CMOS + LD BC,CMOS_AWR + OUT (C),D + LD BC,CMOS_DRD + IN A,(C) + RET + +CMOS_TEST: + + PUSH DE + PUSH BC + PUSH AF + + LD D,3FH + CALL XRD_CMOS + LD E,A + CPL + CALL XWR_CMOS + CALL XRD_CMOS + CPL + CP E + JR NZ,CMOS_ERR + LD A,E + CALL XWR_CMOS + + POP AF + POP BC + POP DE + AND A + RET + +CMOS_ERR: + LD A,E + CALL XWR_CMOS + POP AF + POP BC + POP DE + SCF + RET + +; +;DE - куда - страница открыта! +;BC - сколько +;HL - буфер +; +; PUSH BC +; LD H,D +; LD L,E +; ADD HL,BC +; JR C,CUT +; LD HL,BUFER +; LDIR +; POP BC +; RET +;CUT: +; PUSH HL +; LD A,L +; LD L,C +; LD C,A +; LD A,H +; LD H,B +; LD B,A +; +; AND A +; SBC HL,BC +; LD B,H +; LD C,L +; LD HL,BUFER +; LDIR +; CALL NEXT_BANK +; POP BC +; LD A,B +; OR C +; JR Z,LAB +; LDIR +;LAB: POP BC +; RET +; + +FN_TURBO: + CP 2 + JR Z,FN_TB_ONOFF + CP 3 + JR Z,FN_TB_ONOFF + SCF + RET + +FN_TB_ONOFF: + LD C,A + + IN A,(PAGE3) + LD B,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD DE,(CONFIG_DE) + LD A,E + AND 0FCh + OR C + LD E,A + OUT (CNF_PORT),A + LD (CONFIG_DE),DE + LD A,B + OUT (PAGE3),A + + AND A + RET + +EMM_FN9: +EMM_DIV: ; разделить блок памяти на два блока + ; A - блок, B - длина первого блока после разделения + ; выход: A - блок 1, B - блок 2 + + INC B + DEC B + SCF + RET Z + DEC B + LD E,A + CALL EMM_FN4M ; получить номер страницы блока + RET C + LD D,A + + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + + LD H,RAMD_FAT/256-40H + LD L,D + LD A,(HL) + LD (HL),0FFH + LD B,A + + EX AF,AF' + OUT (PAGE2),A + + LD A,E + AND A + RET + +EMM_FN8: +EMM_ADD: ; слить два блока памяти в один + ; А - блок 1, B - блок 2 + ; выход: А - блок + + LD E,A + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + LD H,RAMD_FAT/256-40H + LD L,E + LD C,B + LD B,0 +EMM_ADD_L: + LD A,(HL) + AND A + JR Z,EMM_ADD_ERR + CP 0FFH + JR Z,EMM_ADD_NEXT + LD L,A + DJNZ EMM_ADD_L +EMM_ADD_ERR: + EX AF,AF' + OUT (PAGE2),A + SCF + RET + +EMM_ADD_NEXT: + LD A,C + AND A + JR Z,EMM_ADD_ERR + LD (HL),A + EX AF,AF' + OUT (PAGE2),A + AND A + LD A,E + RET + + +FN_RESET: ; B - параметр сброса + DEC B ; B = 1 - сброс без перезагрузки схемы. + ; B = 2 - сброс с перезагрузкой схемы + ; ?? B = 3 - сброс с перезагрузкой EP1K30 + JR Z,RESET_1 +; DEC B +; JR Z,RESET_2 + DEC B + RET NZ + INC B +RESET_1: + DI + LD A,0 + LD BC,1FFDH + OUT (C),A + LD B,7FH + OUT (C),A + LD A,40H ;DCP_PAGE + OUT (PAGE3),A + LD A,5 + OUT (PAGE1),A + LD A,2 + OUT (PAGE3),A + LD A,0 + OUT (PAGE0),A + OUT (RGADR),A + OUT (RGMOD),A + ;OUT (#E9),A ; #E9 = RGSCR не реализовано в схеме +; JP Z,0 + JP RESET_ALL + + diff --git a/ZX_EXP/EXP_FN2.ASM b/ZX_EXP/EXP_FN2.ASM new file mode 100644 index 0000000..8abdb5b --- /dev/null +++ b/ZX_EXP/EXP_FN2.ASM @@ -0,0 +1,731 @@ +;***************************************************************** +; +; Функции распределения памяти. +; +;***************************************************************** +; Определение объема памяти. +; Выход: BC - FREE MEM в блоках по 16k, HL - FULL MEM +;***************************************************************** + +EMM_FN0: + IN A,(PAGE2) + LD B,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD HL,RAMD_FAT-4000H + LD C,0 +EMM_FN0_L1: + LD A,(HL) + INC L + JR Z,EMM_FN0_L2 + AND A + JR NZ,EMM_FN0_L1 + INC C + JR EMM_FN0_L1 +EMM_FN0_L2: + LD HL,100H + LD A,B + LD B,0 + OUT (PAGE2),A + RET +; +;***************************************************************** +; Инициализация распределения памяти. +;***************************************************************** +EMM_FN1: + PUSH BC + PUSH HL + PUSH DE + + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD HL,RAMD_FAT-4000H ; Адрес FAT ОЗУ. +EMM_F1_L1: ; Все страницы свободны. + LD (HL),0 + INC L + JR NZ,EMM_F1_L1 +;************************* + LD DE,TAB_PGS ; таблица занятых системных страниц +EMM_F1_L2: + LD A,(DE) + CP 0FFH + JR Z,EMM_F1_L3 + INC DE + LD L,A + LD A,(DE) + LD (HL),A + JR EMM_F1_L2 +EMM_F1_L3: + LD L,A + LD (HL),A +EMM_F1_RAMD_INI: + LD HL,RAMD_KEYS-4000H ; Инициализация ключей RAM-Disks + LD B,RAMD_KEY_NUM +EMM_F1_L5: + LD (HL),0 + INC L + DJNZ EMM_F1_L5 + LD A,C + OUT (PAGE2),A + + POP DE + POP HL + POP BC + RET +;************************************ +TAB_PGS: + DB 0,1,2,3,4,5,6,7 + DB 08H,09H,0AH,0BH,0CH,0DH,0EH,0FH + DB 40H + DB 50H,51H,52H,53H,54H,55H,56H,57H + DB 58H,59H,5AH,5BH,5CH,5DH,5EH,5FH + DB 0FCH,0FDH,0FEH,0FFH + +;***************************************************************** +; Получить блок памяти N bytes, +; Вход: B - число необходимых блоков +; Выход: L,A - КЛЮЧ RAM-Disk/код ошибки +; CF - признак ошибки +;***************************************************************** +EMM_FN2M: + PUSH DE + PUSH BC + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + + LD C,B ; сохранить число нужных блоков + + LD HL,RAMD_FAT-4000H +EMM_F2M_L1: ; Цикл проверки наличия нужных блоков. + DEC L + JR Z,EMM_F2M_NO_RAM + LD A,(HL) + AND A + JR NZ,EMM_F2M_L1 + DJNZ EMM_F2M_L1 + +;************************** +; Место есть ! +;************************** + + LD B,C ; Восстановить нужный объем диска + LD C,0FFH ; МЕТКА КОНЦА RAM-Disk + LD HL,RAMD_FAT-4000H + +EMM_F2M_L2: ; Заполнить RAMD_FAT + DEC L + LD A,(HL) + AND A + JR NZ,EMM_F2M_L2 + LD (HL),C + LD C,L + DJNZ EMM_F2M_L2 + +;************************************ +; L - указатель цепочки. +;************************************ + + EX AF,AF' + OUT (PAGE2),A + LD A,L + AND A + POP BC + POP DE + RET + +EMM_F2M_NO_RAM: + LD L,1 ; НЕТ ПАМЯТИ + EX AF,AF' + OUT (PAGE2),A + LD A,L + SCF + POP BC + POP DE + RET + +;***************************************************************** +; Получить блок памяти N bytes, для RAM-Disk A +; Вход: B - число необходимых блоков, A - RAM-Disk +; Выход: L,A - КЛЮЧ RAM-Disk/код ошибки +; CF - признак ошибки +;***************************************************************** + +EMM_FN2: + PUSH AF + CALL EMM_FN2M + JR C,EMM_FN2_L1 + + LD B,A + POP AF + CALL BLK_TO_RAMD + RET NC +; LD A,B +; CALL EMM_FN3M +; SCF + LD L,2 ; RAM-Disk занят + RET + +EMM_FN2_L1: + POP AF ; нет памяти + LD L,1 + SCF + RET + +;;***************************************************************** +;; Освободить блок памяти +;; Вход: A - НАЧАЛО ЦЕПОЧКИ +;;***************************************************************** +EMM_FN3M: + AND A + SCF + RET Z + + LD L,A + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + + LD H,RAMD_FAT/256-40h + LD A,L +EMM_F3M_L1: + LD L,A + LD A,(HL) ; следующий блок + AND A + JR Z,EMM_FN3M_ERR + LD (HL),0 ; Освободить + CP 0FFH ; Если не конец + JR NZ,EMM_F3M_L1 ; продолжать + + EX AF,AF' + OUT (PAGE2),A + LD A,0 + AND A + RET + +EMM_FN3M_ERR: + EX AF,AF' + OUT (PAGE2),A + LD A,2 + SCF + RET + +;***************************************************************** +; Освободить блок памяти ключа K +; Вход: A - КЛЮЧ RAM-Disk +;***************************************************************** + +EMM_FN3: + CALL GET_RAMD_ST + RET C + SCF + RET Z + LD C,A + IN A,(PAGE2) + LD B,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD (HL),0 + LD A,B + OUT (PAGE2),A + LD A,C + JR EMM_FN3M + +;***************************************************************** +; Получить страницу N блока K +; Вход: A - блок, B - номер страницы +; Выход: A - страница, IF CF - A=0 - нет блока, A=FF - END +;***************************************************************** + +EMM_FN4M: + + LD L,A + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + + INC B + LD H,RAMD_FAT/256-40H +EMM_F4M_L1: + LD A,(HL) + AND A + JR Z,EMM_F4M_ERR + DEC B + JR Z,EMM_F4M_END + LD L,A + CP 0FFH + JR NZ,EMM_F4M_L1 +EMM_F4M_ERR: + LD L,A + EX AF,AF' + OUT (PAGE2),A + LD A,L + SCF + RET + +EMM_F4M_END: + EX AF,AF' + OUT (PAGE2),A + LD A,L + AND A + RET + +;***************************************************************** +; Получить страницу N ключа K +; Вход: A - КЛЮЧ RAM-Disk, B - номер страницы +; Выход: A - страница +;***************************************************************** + +EMM_FN4: + CALL GET_RAMD_ST + RET C + SCF + RET Z + JR EMM_FN4M + +;***************************************************************** +; Получить следующую страницу по FAT +; Вход: A - страница ОЗУ +; Выход: A - следующая страница ОЗУ +;***************************************************************** + +EMM_FN5: + LD L,A + AND A + SCF + RET Z + IN A,(PAGE2) + LD H,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,H + LD H,RAMD_FAT/256-40H + LD L,(HL) + OUT (PAGE2),A + LD A,L + AND A + SCF + RET Z + AND A + RET + +;***************************************************************** +; Получить список страниц блока в HL +; Вход: A - блок, HL - адрес буфера - 256 байт. +; Выход: HL - адрес блока, B - длина блока в страницах ОЗУ +;***************************************************************** + +EMM_FN5M: + PUSH DE + PUSH HL + EX DE,HL + LD B,0 + LD L,A + +EMM_FN5M_L: + LD A,L + LD (DE),A + INC DE + AND A + JR Z,EMM_FN5M_ERR + CP 0FFH + JR Z,EMM_FN5M_END + + IN A,(PAGE2) + LD C,A + LD A,SYS_PAGE + OUT (PAGE2),A + LD H,RAMD_FAT/256-40H + LD L,(HL) + LD A,C + OUT (PAGE2),A + + INC B + JR NZ,EMM_FN5M_L + +EMM_FN5M_ERR: + SCF + POP HL + POP DE + RET + +EMM_FN5M_END: + POP HL + POP DE + AND A + RET + +;***************************************************************** +; Получение адресов портов и данных для восстановления +; Вход: A - номер окна проецирования +; Выход: B - данные, C - адрес порта окна +;***************************************************************** +EMM_FN6: + AND A + LD C,PAGE0 + IN B,(C) + RET Z + DEC A + LD C,PAGE1 + IN B,(C) + RET Z + DEC A + LD C,PAGE2 + IN B,(C) + RET Z + DEC A + LD C,PAGE3 + IN B,(C) + RET Z + SCF + RET + +FN_RAMD_R_W EQU 9BH +BLK_R_W: +RAMD_R_W: +; ╔═══════════════════════════════════╗ +; ║ RD/WR SECTOR HL - BUFER ║\ +; ║ DE - ABS sector в 256b блоках ║\ +; ║ B - число данных в 256b блоках ║\ +; ║ A' - команда чтение/запись ║\ +; ║ A - block RAM ║\ +; ║ 0 - чтение, 255 - запись ║\ +; ║ ** NOT USED TR-DOS VARS ** ║\ +; ╚═══════════════════════════════════╝\ +; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + AND A + SCF + RET C + PUSH HL + PUSH BC + INC D + + LD C,PAGE2 + IN B,(C) + LD A,SYS_PAGE + OUT (C),A + + LD H,82H + LD L,A + +RAMD_LOOP_D: + DEC D + JR Z,NOT_FOUR_BLK + LD L,(HL) + LD L,(HL) + LD L,(HL) + LD L,(HL) + JR RAMD_LOOP_D + +NOT_FOUR_BLK: + LD A,E +NOT_FOUR_BLK1: + SUB 40H + JR C,NOT_ONE_BLK + LD L,(HL) + JR NOT_FOUR_BLK1 + +NOT_ONE_BLK: + AND 3FH +; ADD A,40H + LD D,A ; DE - ADRESS in RAM-Disk + LD E,0 + + LD A,L ; L - текущий банк RAM-Disk + + OUT (C),B ; восстановить страницу + POP BC ; длина данных + POP HL ; адрес буфера + + BIT 7,H + JR NZ,BLK_PAGE1 + +BLK_PAGE3: + LD C,PAGE3 + IN C,(C) + OUT (PAGE3),A + SET 7,D + SET 6,D + JR BLK_CONT1 + +BLK_PAGE1: + LD C,PAGE1 + IN C,(C) + OUT (PAGE1),A + RES 7,D + SET 6,D + +BLK_CONT1: + EX AF,AF' + AND A + JR NZ,NO_EX_RW1 + EX DE,HL +NO_EX_RW1: + EX AF,AF' + + LD A,16 +BLK_LL1: + DUP 16 + LDI + EDUP + DEC A + JR NZ,BLK_LL1 + + EX AF,AF' + JR NZ,NO_EX_RW2 + EX DE,HL +NO_EX_RW2: + EX AF,AF' + + INC B + DEC B + JP Z,BLK_EXIT_1 + + BIT 6,D + JP NZ,BLK_CONT1 + + BIT 7,D + JR Z,BLK_PAGE3_X + + IN A,(PAGE1) + LD E,A + LD D,42H + LD A,SYS_PAGE + OUT (PAGE1),A + LD A,(DE) + OUT (PAGE1),A + LD DE,4000H + JP BLK_CONT1 + +BLK_PAGE3_X: + IN A,(PAGE3) + LD E,A + LD D,0C2H + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(DE) + OUT (PAGE3),A + LD DE,0C000H + + BIT 7,H + JP Z,BLK_CONT1 + + LD E,A + LD A,C + OUT (PAGE3),A + IN A,(PAGE1) + LD C,A + LD A,E + OUT (PAGE1),A + LD DE,4000H + JP BLK_CONT1 + +BLK_EXIT_1: + LD A,D + RLCA + XOR D + RLCA + JR NC,BLK_EXIT_2 + + LD A,C + OUT (PAGE1),A + AND A + RET +BLK_EXIT_2: + LD A,C + OUT (PAGE3),A + AND A + RET + + +DISK_REDIR: + PUSH HL + LD HL,DISK_TYPE-4000h + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,(OPER_DISK) + AND 3 + ADD A,L + LD L,A + LD L,(HL) + EX AF,AF' + OUT (PAGE2),A + LD A,L + POP HL + RET + +SET_DISK_TYPE: + PUSH HL + LD HL,DISK_TYPE-4000H + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + LD A,(OPER_DISK) + AND 3 + ADD A,L + LD L,A + LD (HL),E + LD L,(HL) + EX AF,AF' + OUT (PAGE2),A + LD A,L + POP HL + RET + + +RAMD_CALC_PAGE: ; вход: DE - ram_disk trk_sec, A - RAM-Disk + ; выход: HL - адрес, A - page + CP 16 + CCF + RET C ; НЕТ ТАКОГО RAM-Disk + + PUSH AF + LD H,D + LD L,E + ADD HL,HL + ADD HL,HL + LD B,H + LD A,E + OR 0C0H + LD C,A ; C - часть адреса, B - страница + POP AF + + CALL EMM_FN4 ; ПОЛУЧИТЬ СТРАНИЦУ + LD L,0 + LD H,C + +; RET C +; LD B,A +; LD A,B +; CP 0FFH +; CCF + + RET + +;***************************************************** +; +; HL - буфер +; +;***************************************************** + +Version EQU 020DH ; Version BIOS + +ID_SPRINTER: + DB 'Sprinter BIOS: ver 2.13',0,"Sprinter",0,0 +L_ID EQU $-ID_SPRINTER +;***************************************************** + +FN_VERSION: + PUSH HL + EX DE,HL + LD HL,ID_SPRINTER + LD BC,L_ID + LDIR + POP HL + LD DE,Version + + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + LD BC,(CONFIG_BYTE-4000h) ; Version PLD + EX AF,AF' + OUT (PAGE2),A + LD A,2 ; Число полей + AND A + RET + +;***************************************************** + +EMM_FN7: + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + LD HL,08000H + LD DE,ID_SPRINTER + LD B,L_ID +ID_LOOP: + LD A,(DE) + CP (HL) + JR NZ,INIT_SYS_ALL + INC HL + INC DE + DJNZ ID_LOOP + EX AF,AF' + OUT (PAGE2),A + RET + + +MSDOS_COLD_VARS: + DI + IN A,(PAGE2) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE2),A + +INIT_SYS_ALL: + LD DE,08000H + LD HL,ID_SPRINTER + LD BC,L_ID + LDIR + + LD HL,DISK_TYPE-4000h + LD (HL),0 + INC HL + LD (HL),1 + INC HL + LD (HL),40H ; HDD + INC HL + LD (HL),3 + INC HL + LD (HL),0 ; PAGE0 + INC HL + LD (HL),5 ; PAGE1 + INC HL + LD (HL),2 ; PAGE2 + INC HL + LD (HL),0 ; PAGE3 + + LD A,9 + LD (MSD_SECS-4000h),A + LD A,0 + LD (DS_1440-4000h),A + + LD HL,RAMD_KEYS-4000h + LD DE,RAMD_KEYS+1-4000h + + LD BC,RAMD_KEY_NUM-1 + LD (HL),0 + LDIR + + LD DE,ZG_ADRESS + LD (WIN_ZG-4000h),DE + LD HL,L_SCORPION + LD (CONFIG_ALL-4000h),HL + LD DE,CNF_0 + LD (CONFIG_DE-4000h),DE + + LD HL,TASK_DATA-4000h ; убить все задачи + LD DE,TASK_DATA+1-4000h + LD BC,0FFH + LD (HL),0 + LDIR + + EX AF,AF' + OUT (PAGE2),A + + CALL EMM_FN1 + RET + + + diff --git a/ZX_EXP/EXP_HDD.ASM b/ZX_EXP/EXP_HDD.ASM new file mode 100644 index 0000000..24d029c --- /dev/null +++ b/ZX_EXP/EXP_HDD.ASM @@ -0,0 +1,673 @@ +;;.PRINTX "HDD utility" + + DEFINE HDD_TYPE 1 + + MACRO WAIT_HDD +.LL1: + LD BC,P_HDST + IN A,(C) + BIT 7,A + JR NZ,.LL1 + ENDM + + +;************************************** +HD_EXP_EXE: + POP AF + CALL HD_CMD_X + JP HD_EXP_RET + +HD_CMD: + POP AF + CALL HD_CMD_X + JP HD_DOS_RET + + +HD_CMD_X: ; C - COMAND ( 5 - RD, 6 - WR ) + PUSH AF + LD A,C ; 0 - INIT, 1 - DIAG + AND A + JP Z,HD_CMD_0 ; инициализация + DEC A + JP Z,HD_CMD_1 ; ?? + DEC A + JP Z,HD_CMD_2 ; тест + DEC A + JP Z,HD_CMD_3 ; preread + DEC A + JP Z,HD_CMD_4 ; read bpb + DEC A + JP Z,HD_CMD_5 ; read + DEC A + JP Z,HD_CMD_6 ; write + DEC A + JP Z,FN_HDD_PART + POP AF + SCF + RET + +HD_BPB_PREP: + + LD D,A + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(HD_IDF_ADR+12) + LD E,A + EX AF,AF' + OUT (PAGE3),A + LD A,D + LD D,0 + LD IX,0 + LD B,1 + +HD_PREPARE: + + PUSH AF + PUSH HL + CALL HD_CALC_SECS + JR NC,HD_PREP_L1 + POP HL + POP AF + SCF + RET + +HD_PREP_L1: + + LD A,B + LD BC,P_S_CNT ; Установить число секторов для чтения + OUT (C),A + LD BC,P_S_NUM + OUT (C),L ; СЕКТОР + LD BC,P_C_LOW + OUT (C),E ; дорожка low + LD BC,P_C_HIG + OUT (C),D ; дорожка high + + LD BC,P_HD_CS + DEC B + IN A,(C) + AND 0F0H + OR H + INC B + + OUT (C),A + + POP HL ; BUFER & PAGE + POP AF + AND A + RET + +HD_CMD_3: ; ПОДГОТОВКА К ВНЕШНИМ ОПЕРАЦИЯМ R/W + POP AF + + AND A + INC B + DEC B + RET Z + CALL HD_WAIT + RET C + CALL HD_PREPARE + RET C + + EXX + LD C,PAGE3 + IN B,(C) + EXX + OUT (PAGE3),A + EX AF,AF' + + LD A,0C0H + OUT (RGADR),A + + LD BC,P_CMD + LD A,20H +; OUT (C),A + + AND A + RET + +; SCF +; RET + + + +HD_CMD_4: + POP AF + CALL HD_WAIT + RET C + CALL HD_BPB_PREP + RET C + + JR HD_RD_L1 + +;******************* +HD_CMD_5: + POP AF ; HL - BUFER, A - PAGE !!! + +HD_RD_CMD: + AND A + INC B + DEC B + RET Z + CALL HD_WAIT + RET C + CALL HD_PREPARE + RET C + +HD_RD_L1: + + EXX + LD C,PAGE3 + IN B,(C) + EXX + OUT (PAGE3),A + EX AF,AF' + + LD A,0C0H + OUT (RGADR),A + + LD BC,P_CMD + LD A,20H + OUT (C),A + +HD_RD_L2: + WAIT_HDD + + BIT 3,A + JR NZ,HD_READ_CONT + + LD A,0 + OUT (RGADR),A +HD_RET: + EXX + OUT (C),B + EXX + LD BC,P_ERR + IN A,(C) + AND A + SCF + RET NZ + + EX AF,AF' + AND A + RET + +HD_READ_CONT: + LD BC,P_DATS + +HD_RD_CONT1: + DUP 16 + INI ; всего 16 раз INI - оптимально. + EDUP + JP NZ,HD_RD_CONT1 +HD_RD_CONT2: + DUP 16 + INI ; всего 16 раз INI - оптимально. + EDUP + JP NZ,HD_RD_CONT2 + +; INIR +; INIR + + LD A,H + OR L + JR NZ,HD_RD_L2 + + LD A,SYS_PAGE + OUT (PAGE3),A + EX AF,AF' + LD HL,RAMD_FAT + LD L,A + LD A,(HL) + OUT (PAGE3),A + EX AF,AF' + LD HL,0C000H + JR HD_RD_L2 + +;*********************************************** + +HD_CMD_6: + POP AF ; HL - BUFER, A - PAGE !!! + +HD_WR_CMD: + AND A + INC B + DEC B + RET Z + + CALL HD_WAIT + RET C + CALL HD_PREPARE + RET C + + EXX + LD C,PAGE3 + IN B,(C) + EXX + OUT (PAGE3),A + EX AF,AF' + + LD BC,P_CMD + LD A,30H + OUT (C),A + +HD_WR_L2: + WAIT_HDD + + BIT 3,A + JP Z,HD_RET + +;####################################### + IF HDD_TYPE + + LD BC,P_DATS+100H + LD D,32 +HD_WR_CONT1: + DUP 16 + OUTI ; всего 16 раз OUTI - оптимально. + EDUP + DEC D + JP NZ,HD_WR_CONT1 + +;####################################### + ELSE + LD D,64 + LD BC,P_DATS+100H + +HD_WR_LOOP: + + INC HL + OUTI + DEC HL + DEC HL + OUTI + INC HL + + INC HL + OUTI + DEC HL + DEC HL + OUTI + INC HL + + INC HL + OUTI + DEC HL + DEC HL + OUTI + INC HL + + INC HL + OUTI + DEC HL + DEC HL + OUTI + INC HL + + DEC D + JR NZ,HD_WR_LOOP + ENDIF + +;####################################### + LD A,H + OR L + JR NZ,HD_WR_L2 + + LD A,SYS_PAGE + OUT (PAGE3),A + EX AF,AF' + LD HL,RAMD_FAT + LD L,A + LD A,(HL) + OUT (PAGE3),A + EX AF,AF' + + LD HL,0C000H + JR HD_WR_L2 + +;******************************************************** +HD_CMD_1: + + POP AF +HD_CMD1_EX: + ld a,0A0h + LD BC,P_HD_CS + OUT (C),A + LD A,90h ; DIAGNOSTICS + CALL HD_CMD_EXE + AND A + BIT 0,A + RET Z + LD BC,P_ERR + IN A,(C) + CP 1 + RET Z + SCF + RET + +;********************************************** + +HD_CMD_2: + POP AF + + LD E,0 + + LD BC,P_HD_CS + LD A,0A0H + OUT (C),A + + CALL TEST_HDD_DRV + + JR NZ,NO_HDD1 + SET 0,E +NO_HDD1: + + LD BC,P_HD_CS + LD A,0B0H + OUT (C),A + + CALL TEST_HDD_DRV + + JR NZ,NO_HDD2 + SET 1,E +NO_HDD2: + + LD A,E + AND A + SCF + RET Z ; HDD absent ! + AND A + RET + +TEST_HDD_DRV: + LD HL,01FEH + LD BC,P_S_CNT + OUT (C),L + LD BC,P_S_NUM + OUT (C),H + LD BC,P_S_CNT+256 + IN A,(C) + CP L + RET NZ + LD BC,P_S_NUM+256 + IN A,(C) + CP H + RET + +HD_CMD_0: + + POP AF + + LD BC,P_HD_CS + LD A,0A0H + OUT (C),A + + CALL TEST_HDD_DRV + + JR NZ,HD_ABSENT + +HD_C0_L3: + + WAIT_HDD + + LD BC,P_CMD + LD A,0ECH + OUT (C),A + + WAIT_HDD +;HD_C0_L1: +; +; LD BC,P_HDST +; IN A,(C) +; BIT 7,A +; JR NZ,HD_C0_L1 + + BIT 3,A + JR NZ,HD_C0_L2 + + SCF + RET + +HD_ABSENT: + LD BC,P_HD_CS + LD A,0B0H + OUT (C),A + + CALL TEST_HDD_DRV + JR Z,HD_C0_L3 + + SCF + RET + +HD_C0_L2: + LD BC,P_DATS + LD HL,HD_IDF_ADR + IN A,(PAGE3) + LD D,A + LD A,SYS_PAGE + OUT (PAGE3),A + INIR + INIR + +;HD_C0_L4: +; LD BC,P_HDST +; IN A,(C) +; BIT 7,A +; JR NZ,HD_C0_L4 + + LD A,(HD_IDF_ADR+12) ; число секторов + LD C,A + LD B,0 + LD HL,0 + LD A,(HD_IDF_ADR+6) ; число головок +LOOP_XX: + ADD HL,BC + DEC A + JR NZ,LOOP_XX + LD (HD_IDF_ADR+4),HL + + WAIT_HDD + + LD BC,P_HD_CS + DEC B + IN A,(C) + AND 010H + LD B,A + LD A,(HD_IDF_ADR+6) ; число головок + + DEC A + AND 0FH + OR 0A0H + OR B + + LD H,A + LD A,(HD_IDF_ADR+99) + BIT 1,A + JR Z,HD_C0_NO_LBA + + SET 6,H + +HD_C0_NO_LBA: + LD BC,P_HD_CS + OUT (C),H + + LD A,(HD_IDF_ADR+12) ; число секторов + + LD BC,P_S_CNT + OUT (C),A + LD A,D + OUT (PAGE3),A + + LD A,91H ; SET HDD PARAMETERS + CALL HD_CMD_EXE + RET + +; RET C +; LD A,1FH ; RECALIBRATE +; CALL HD_CMD_EXE +; RET + +HD_CMD_EXE: + CALL HD_WAIT + RET C + LD BC,P_CMD + OUT (C),A +HD_WAIT: + PUSH DE + PUSH BC + PUSH AF + + LD DE,0 + +HD_WAIT1: + LD BC,P_HDST + IN A,(C) + BIT 7,A + JR Z,HD_W_EXIT + DEC DE + LD A,D + OR E + JR NZ,HD_WAIT1 + + POP AF + POP BC + POP DE + SCF + RET + +HD_W_EXIT: + POP AF + POP BC + POP DE + AND A + RET + +;**************************** +HDD_LBA: + POP BC + LD L,E + LD E,D + DB 0DDH + LD D,L + XOR A + LD H,A + RET + + +HD_CALC_SECS: + + DB 0DDH + LD A,H + AND A + SCF + RET NZ ; ошибка, слишком большой HDD.. + + PUSH BC + LD BC,P_HD_CS + DEC B + IN A,(C) + BIT 6,A + JR NZ,HDD_LBA + +;******************************* +; IX,DE - абсолютный номер сектора + + PUSH IX + POP HL + + IN A,(PAGE3) + LD C,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,C + LD (COPY_PAGE3),A + + LD BC,(HD_IDF_ADR+4) ; число секторов на цилиндре + +; HL,DE - номер сектора +; BC - число секторов на цилиндре +; HL,DE разделить на BC + LD A,16 + SCF +DIV_LOOP: + EX DE,HL + ADD HL,HL + EX DE,HL + ADC HL,HL + + SBC HL,BC ; сравнить HL и BC + JR NC,NO_ADD ; переноса не было - +1! + ADD HL,BC + DEC A + JR NZ,DIV_LOOP + JR DIV_END +NO_ADD: INC DE + DEC A + JR NZ,DIV_LOOP +DIV_END: ; DE - результат, HL - остаток + +; DE - цилиндр + LD A,(HD_IDF_ADR+12) ; A - число секторов на дорожке +; HL - номер сектора в цилиндре + LD B,0 + LD C,A + +; LD BC,(MS_BPB+S_P_T) ; ЧИСЛО СЕКТОРОВ НА ДОРОЖКЕ + XOR A +HD_CALC_LOOP2: + SBC HL,BC + INC A + JR NC,HD_CALC_LOOP2 + + DEC A ; A - головка + ADD HL,BC ; L - сектор + INC L + LD H,A ; HL - HEAD,SEC + + LD A,(COPY_PAGE3) + OUT (PAGE3),A + + POP BC + AND A + RET + +FN_HDD_PART: + POP AF + BIT 0,A + LD A,#21 ; !HARDCODE + JR Z,.SET_CH + LD A,#01 ; !HARDCODE +.SET_CH: + OUT (#BC),A ; !HARDCODE IDE.Chanel.Choose + RET +; DB 'HDD_DRV_END' + +; Вход: +; C - команда +; 0 - INIT - входных пар нет => A - число поддерживаемых дисков. +; 1 - RESET - выбор диска A - номер диска от 0 => +; 2 - (STATUS) +; 3 - MEDIA CHECK - A - номер диска => A = 0 - old. #FF - new (всегда #FF) +; 4 - READ BPB - A - диск HL - адрес в текущей странице. +; 5 - READ - A - диск, IX:DE сектор, HL - адрес, B - число секторов +; 6 - WRITE - '' - +; +; ошибки - CF - A - ошибка +; +; 0 - нет ошибки +; 1 - неверная команда +; 2 - неверный номер диска +; 3 - неверный формат (не MS-DOS) +; 4 - нет готовности +; 5 - ошибка позиционирования +; 6 - сектор не найден +; 7 - ошибка CRC +; 8 - защита записи +; 9 - ошибка чтения +; 10 - ошибка записи +; 11 - ГЛЮК +; + + diff --git a/ZX_EXP/EXP_LP2.ASM b/ZX_EXP/EXP_LP2.ASM new file mode 100644 index 0000000..1e7b664 --- /dev/null +++ b/ZX_EXP/EXP_LP2.ASM @@ -0,0 +1,2012 @@ +;;.PRINTX "Low-print-2." +;============================================================== +; +; Программа LOW PRINT for Sprinter-97. +; +;============================================================== + +;============================================================= +; Функции Bios Sprinter-97. +; Рабочая версия 29.05.97 +;============================================================= +; LP_OPEN_WIN Открытие окна +; point 3D13h C=80h Вход: B - код окна. +; B=0 - Spectrum 32x24 B=1 - Spectrum 64x24 +; B=2 - Spectrum 40x24 B=3 - Spectrum 80x24 +; Выход: DE - размер окна +;============================================================= +; LP_PRINT_ALL Вывод символов на экран с текущего знакоместа +; point 3D13h C=81h Вход: A - символ, +; B - число выводимых символов, +; Е - атрибут символа +;============================================================= +; LP_PRINT_SYM Вывод символов на экран с текущего знакоместа +; без атрибута +; point 3D13h C=82h Вход: A - символ, +; B - число выводимых символов, +;============================================================= +; LP_PRINT_ATR Вывод атрибутов на экран с текущего знакоместа +; point 3D13h C=83h Вход: B - число выводимых символов, +; Е - атрибут символа +;============================================================= +; LP_SET_PLACE Установка текущего знакоместа +; point 3D13h C=84h Вход: E - знакоместо по горизонтали +; D - знакоместо по вертикали +;============================================================= +; LP_PRINT_LN Вывод строки символов на экран с текущего +; знакоместа +; point 3D13h C=85h Вход: HL - адрес строки +; B - число выводимых символов, +; Е - атрибут символов +;============================================================= +; LP_PRINT_LN2 Вывод строки символов на экран с текущего +; знакоместа без атрибутов +; point 3D13h C=86h Вход: HL - адрес строки +; B - число выводимых символов, +;============================================================= +; LP_PRINT_LN3 Вывод строки символов на экран с текущего +; знакоместа +; point 3D13h C=87h Вход: HL - адрес строки +; B - число выводимых символов, +; D - разделитель +; Е - атрибут символов +;============================================================= +; LP_PRINT_LN4 Вывод строки символов на экран с текущего +; знакоместа без атрибутов +; point 3D13h C=88h Вход: HL - адрес строки +; B - число выводимых символов, +; D - разделитель +;============================================================= +; LP_CLS_WIN Очистка окна экрана +; point 3D13h C=89h Вход: DE - положение окна +; B - атрибут очистки +; H - высота, L - ширина +;============================================================= +TEST_40 MACRO +; LOCAL LLL +; EX AF,AF' +; JR C,LLL +; INC D ; режим 40 +;LLL: +; EX AF,AF' + ENDM + +DJ_NEXT_HL MACRO + DJNZ .LLL + CALL LP_NEXT_HL +.LLL: + ENDM + + + +LP_OPEN_PG MACRO + IN A,(PAGE3) + LD C,A + LD A,SYS_PAGE + OUT (PAGE3),A + + LD A,C + LD (COPY_PAGE3),A + IN A,(RGADR) + LD (COPY_RGADR),A + + ENDM + +LP_CLOSE_PG MACRO + + LD A,(COPY_RGADR) + OUT (RGADR),A + LD A,(COPY_PAGE3) + OUT (PAGE3),A + + ENDM + + +LP_BEG_PM MACRO + + EX AF,AF' + EXX + + IN A,(PAGE3) + LD C,A + IN A,(RGADR) + LD B,A + + LD A,SYS_PAGE + OUT (PAGE3),A + LD (SYS_SP),SP + LD SP,SYS_SP + PUSH BC + + LD HL,(WIN_MAP_IX+WIN_HL) + LD DE,(WIN_MAP_IX+WIN_DE) + LD BC,(WIN_MAP_IX+WIN_BC) + + LD A,E + AND A + RRA + OUT (RGADR),A + EXX + EX AF,AF' + + ENDM + +LP_END_PM MACRO + + EX AF,AF' + EXX + + RLA + LD E,A + LD (WIN_MAP_IX+WIN_HL),HL ; место печати + LD (WIN_MAP_IX+WIN_DE),DE + LD (WIN_MAP_IX+WIN_BC),BC + + POP BC + LD A,B + OUT (RGADR),A + LD A,C + OUT (PAGE3),A + + LD SP,SYS_SP + + EXX + EX AF,AF' + RET + + ENDM + +LP_PRINT_ALL: + CALL LP_BEG_P + EXX + LD C,A + LD A,50H + OUT (PAGE3),A + EXX + +LP_PRINT_AL1: + + EXX + LD A,D + OUT (RGADR),A + INC D + LD (HL),C + EXX + LD A,E + EXX + INC L + LD (HL),A + DEC L + +; TEST_40 + + DJ_NEXT_HL + +; DEC B +; CALL Z,LP_NEXT_HL + EXX + + DJNZ LP_PRINT_AL1 + + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + +LP_PRINT_SYM: + + CALL LP_BEG_P + EXX + LD C,A + LD A,50H + OUT (PAGE3),A + EXX +LP_PRINT_SY1: + EXX + LD A,D + OUT (RGADR),A + LD (HL),C + INC D + + TEST_40 + + DJ_NEXT_HL +; DEC B +; CALL Z,LP_NEXT_HL + EXX + DJNZ LP_PRINT_SY1 + + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + +LP_PRINT_ATR: + CALL LP_BEG_P + LD A,E + EXX + LD C,A + LD A,50H + OUT (PAGE3),A + EXX + +LP_PRINT_AT1: + EXX + LD A,D + OUT (RGADR),A + INC D + + INC L + LD (HL),C + DEC L + + + TEST_40 + + DJ_NEXT_HL +; DEC B +; CALL Z,LP_NEXT_HL + EXX + DJNZ LP_PRINT_AT1 + + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + + +LP_PRINT_LINE: + CALL LP_BEG_P + EXX +; LD C,A + LD A,50H + OUT (PAGE3),A + LD C,RGADR + EXX + +LP_PRINT_LN1: + EXX + OUT (C),D + INC D + EXX + LD A,(HL) + INC HL + EXX + LD (HL),A + EXX + LD A,E + EXX + INC L + LD (HL),A + DEC L + + TEST_40 + + DJ_NEXT_HL + +; DEC B +; CALL Z,LP_NEXT_HL + EXX + + DJNZ LP_PRINT_LN1 + + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + + +LP_PRINT_LINE2: + CALL LP_BEG_P + + LD A,50H + OUT (PAGE3),A + EXX + LD C,RGADR + EXX + +LP_PRINT_LN2: + LD A,(HL) + INC HL + EXX + OUT (C),D + INC D + LD (HL),A + TEST_40 + + DJ_NEXT_HL + +; DEC B +; CALL Z,LP_NEXT_HL + EXX + DJNZ LP_PRINT_LN2 + + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + +LP_PRINT_LINE3: + CALL LP_BEG_P + EXX +; LD C,A + LD A,50H + OUT (PAGE3),A + EXX + +LP_PRINT_LN3: + EXX + LD A,D + OUT (RGADR),A + INC D + + EXX + LD A,(HL) + INC HL + CP D + JR NZ,LP_PR_L31 + DEC HL + LD A,' ' +LP_PR_L31: + EXX + + LD (HL),A + EXX + LD A,E + EXX + INC L + LD (HL),A + DEC L + + TEST_40 + + DJ_NEXT_HL + +; DEC B +; CALL Z,LP_NEXT_HL + EXX + + DJNZ LP_PRINT_LN3 + + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + + +LP_PRINT_LINE4: + CALL LP_BEG_P + EXX +; LD C,A + LD A,50H + OUT (PAGE3),A + EXX + +LP_PRINT_LN4: + EXX + LD A,D + OUT (RGADR),A + + EXX + LD A,(HL) + INC HL + CP D + JR NZ,LP_PR_L41 + DEC HL + LD A,' ' +LP_PR_L41: + EXX + + LD (HL),A + + INC D + + TEST_40 + + DJ_NEXT_HL + +; DEC B +; CALL Z,LP_NEXT_HL + EXX + + DJNZ LP_PRINT_LN4 + + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + + +LP_PRINT_LINE5: + CALL LP_BEG_P + EXX +; LD C,A + LD A,50H + OUT (PAGE3),A + EXX + +LP_PRINT_LN5: + EXX + LD A,D + OUT (RGADR),A + + EXX + LD A,(HL) + INC HL + CP D + JR Z,LP_PR_L51 + EXX + + LD (HL),A + EXX + LD A,E + EXX + INC L + LD (HL),A + DEC L + + INC D + + TEST_40 + + DJ_NEXT_HL + +; DEC B +; CALL Z,LP_NEXT_HL + EXX + + DJNZ LP_PRINT_LN5 + +LP_PR_L51: + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + +LP_PRINT_LINE6: + CALL LP_BEG_P + EXX +; LD C,A + LD A,50H + OUT (PAGE3),A + EXX + +LP_PRINT_LN6: + EXX + LD A,D + OUT (RGADR),A + + EXX + LD A,(HL) + INC HL + CP D + JR Z,LP_PR_L61 + EXX + + LD (HL),A + + INC D + + TEST_40 + + DJ_NEXT_HL + +; DEC B +; CALL Z,LP_NEXT_HL + EXX + + DJNZ LP_PRINT_LN6 +LP_PR_L61: + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + +;************************************* + +LP_PRINT_LINE_DIR: + + CALL LP_BEG_P + EXX +; LD C,A + LD A,50H + OUT (PAGE3),A + EXX + +LP_PRINT_LN_D: + EXX + LD A,D + OUT (RGADR),A + + EXX + LD A,(HL) + INC HL + CP B + JR Z,LP_LN_DD1 + EXX + + CP 14 + JR NC,LP_PRINT_LN_DD + CP 7 + JR C,LP_PRINT_LN_DD + SUB 7 + JR Z,LP_BEEP + DEC A + JR Z,LP_BACK + DEC A + JR Z,LP_TAB + DEC A + JR Z,LP_LF + DEC A + JR Z,LP_XX +LP_PRINT_LN_D2: + DEC A + JR Z,LP_CLS + DEC A + JR Z,LP_CR + +LP_XX: ; выводить! + +LP_PRINT_LN_DD: + LD (HL),A + INC D + +LP_PRINT_LN_D1: + DJ_NEXT_HL + EXX + JR LP_PRINT_LN_D + +LP_LN_DD1: + LD A,SYS_PAGE + OUT (PAGE3),A + CALL LP_END_P + RET + +LP_PRINT_LN_D11: + LD A,50H + OUT (PAGE3),A + JR LP_PRINT_LN_D1 + +LP_BEEP: + JR LP_PRINT_LN_D2 + +LP_BACK: + LD A,SYS_PAGE + OUT (PAGE3),A + + LD A,(WIN_MAP_IX+WIN_H_BEG) + CP D + JR Z,LP_PRINT_LN_D11 + INC B + DEC D + JR LP_PRINT_LN_D11 + +LP_TAB: + LD A,SYS_PAGE + OUT (PAGE3),A + + LD A,(WIN_MAP_IX+WIN_H_BEG) + SUB D + NEG + AND 07H + NEG + ADD A,8 + + LD C,A + LD A,50H + OUT (PAGE3),A + +LP_TAB_L: + LD (HL),20H + INC D + DEC B + JR Z,LP_TAB_L1 + DEC C + JR NZ,LP_TAB_L + JR LP_PRINT_LN_D1 + +LP_TAB_L1: + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(WIN_MAP_IX+WIN_H_BEG) + LD D,A + LD A,(WIN_MAP_IX+WIN_SIZE_REL) + LD B,A + +LP_LF: + LD A,SYS_PAGE + OUT (PAGE3),A + + INC L + INC L + INC L + INC L + + LD A,(WIN_MAP_IX+WIN_V_END) + CP L +LP_CLS: + JR NC,LP_PRINT_LN_D11 + + LD A,(WIN_MAP_IX+WIN_V_BEG) + LD L,A ; вернуться наверх без скролла!!! + +; scrolling ???? + + JR LP_PRINT_LN_D11 + +; LP_CLS: + + JR LP_PRINT_LN_D1 + +LP_CR: + LD A,SYS_PAGE + OUT (PAGE3),A + + LD A,(WIN_MAP_IX+WIN_H_BEG) + LD D,A + LD A,(WIN_MAP_IX+WIN_SIZE_REL) + LD B,A + + JR LP_PRINT_LN_D11 + +;***************************************** + +LP_SET_PLACE: + CALL LP_BEG_P + CALL LP_AT_D + CALL LP_TAB_E + CALL LP_END_P + RET + +LP_GET_PLACE: + CALL LP_BEG_P + + LD A,(WIN_MAP_IX+WIN_H_BEG) + NEG + EXX + ADD A,D + EXX + LD E,A + + LD A,(WIN_MAP_IX+WIN_V_BEG) + NEG + EXX + ADD A,L + DEC A + EXX + RRCA + RRCA + AND 3FH + LD D,A + + CALL LP_END_P + RET + +;********************************************* + + +;********************************************* + + + +LP_CLS_WIN: + LD C,20h + JR LP_CLS_WIN_3 +LP_CLS_WIN_2: + LD C,A +LP_CLS_WIN_3: + CALL LP_BEG_P + LD (SYS_WORK1),BC ; сохранить цвет + push de +LP_CLS_L2: + CALL LP_AT_D + CALL LP_TAB_E + PUSH DE + + EXX + LD BC,(SYS_WORK1) + EXX + LD B,L + + LD A,50H + OUT (PAGE3),A + +LP_CLS_L1: + + EXX + LD A,D + OUT (RGADR),A + + LD (HL),C + INC L + LD (HL),B + DEC L + + INC D + EXX + + DJNZ LP_CLS_L1 + + LD A,SYS_PAGE + OUT (PAGE3),A + + POP DE + INC D + DEC H + JR NZ,LP_CLS_L2 + pop de + + CALL LP_AT_D + CALL LP_TAB_E + CALL LP_END_P + RET + +;******************************************************* +; +; WIN_MAP_SC - карта экрана :-) +; 0 - hor size +; 1 - ver size +; 2 - hor place +; 3 - ver place + +LP_SIZE: ; определение size + LP_OPEN_PG + LD DE,(WIN_MAP_IX+WIN_SIZE_H) + LP_CLOSE_PG + RET + +; LD (WIN_MODE_SC),DE ; место по горизонтали и страница моды +; LD (WIN_MODE_SH),HL ; место по вертикали с адресом + +LP_AT_D: + + LD A,(WIN_MAP_IX+WIN_SIZE_V) + EXX + LD L,A + EXX + LD A,D + EXX +LP_AT_DX: + SUB L + JR NC,LP_AT_DX +; JR Z,LP_AT_DX + ADD A,L + + ADD A,A + ADD A,A + LD L,A + LD A,(WIN_MAP_IX+WIN_V_BEG) + ADD A,L + LD L,A + INC L + LD H,0C3H + EXX + RET + +LP_TAB_E: + LD A,(WIN_MAP_IX+WIN_SIZE_REL) + EXX + LD D,A + EXX + LD A,E + EXX +LP_TAB_EX: + SUB D + JR NC,LP_TAB_EX + JR Z,LP_TAB_EX + NEG + LD B,A ; сохранить сколько осталось символов в строке + NEG + ADD A,D + LD D,A + LD A,(WIN_MAP_IX+WIN_MODE) + BIT 5,A + JR Z,LP_NO_ADD_A + + LD A,D + ADD A,A + LD D,A + +LP_NO_ADD_A: + LD A,(WIN_MAP_IX+WIN_H_BEG) + ADD A,D + LD D,A + EXX + RET + + +;************************************************************* +;******************************************* + +LP_NEXT_HL: ; вычисление нового места + LD A,SYS_PAGE + OUT (PAGE3),A + + INC L + INC L + INC L + INC L + + LD A,(WIN_MAP_IX+WIN_V_END) + CP L + JR NC,LP_NEXT_HL1 + + LD A,(WIN_MAP_IX+WIN_V_BEG) + LD L,A + INC L + LD H,0C3H + +LP_NEXT_HL1: + LD A,(WIN_MAP_IX+WIN_H_BEG) + LD D,A + + LD A,(WIN_MAP_IX+WIN_SIZE_REL) + LD B,A + + LD A,50H + OUT (PAGE3),A + RET + +; program set registers +; for low printer +LP_BEG_P: + EX AF,AF' + EXX + LP_OPEN_PG + LD HL,(WIN_MAP_IX+WIN_HL) + LD DE,(WIN_MAP_IX+WIN_DE) + LD BC,(WIN_MAP_IX+WIN_BC) + LD A,E + AND A + RRA + OUT (RGADR),A + EXX + EX AF,AF' + RET + +; program init registers +; for low printer & save + +WIN_GET_ZG: ; DE - адрес куда переслать фонт + LD HL,ZG_ADRESS + LD BC,800H + LDIR + AND A + RET + +WIN_SET_ZG: +LP_SET_ZG: + EX AF,AF' + EXX + LP_OPEN_PG + + CALL LP_SET_ZG1 + + LP_CLOSE_PG + EXX + EX AF,AF' + AND A + RET + + +LP_SET_ZG1: + IN A,(PAGE1) + LD (COPY_PAGE1),A + LD A,0FFH + OUT (PAGE1),A + + EXX + EX AF,AF' + + LD B,A + AND 0FH + ADD A,A + OUT (RGADR),A + LD A,B + RRCA + RRCA + RRCA + AND 18H + OR 040H + LD H,A + LD L,0 + + LD BC,0800H + EX DE,HL + LDIR + EX DE,HL + LD A,H + RRCA + RRCA + RRCA + DEC A + AND 3 + ADD A,058H + LD H,A +; LD HL,05800H +LP_INI_L1: + LD (HL),L + INC L + JR NZ,LP_INI_L1 + + EXX + + EX AF,AF' + LD A,(COPY_PAGE1) + OUT (PAGE1),A + + RET + +LP_INI_P: + + EX AF,AF' + EXX + LP_OPEN_PG + + LD A,(WIN_MAP_IX+WIN_MODE) + CP 0C0H + JR NC,LP_INI_NO_ZG ; открытие бордера... + LD A,(WIN_MAP_IX+WIN_MODE_S) + BIT 0,A + JR NZ,LP_INI_NO_ZG ; открытие Spectrum-Screen + + LD A,(WIN_MAP_IX+WIN_MODE) + BIT 4,A + JR Z,LP_INI_NO_ZG ; открытие графического экрана + LD DE,(WIN_ZG) ; знакогенератор + + EXX + EX AF,AF' + CALL LP_SET_ZG1 + EXX + EX AF,AF' + +LP_INI_NO_ZG: + LD A,(WIN_MAP_IX+WIN_H_BEG) + LD D,A + LD A,(WIN_MAP_IX+WIN_V_BEG) + LD L,A + INC L + LD H,0C3H + + AND A + LD A,(WIN_MAP_IX+WIN_MODE) + BIT 5,A + LD A,(WIN_MAP_IX+WIN_SIZE_H) + JR NZ,LP_INI_40 + ADD A,A + SCF +LP_INI_40: + LD B,A + LD (WIN_MAP_IX+WIN_SIZE_REL),A + +; LD DE,(WIN_MODE_SC) ; место по горизонтали и страница моды +; LD HL,(WIN_MODE_SH) ; место по вертикали с адресом +; INC HL +; LD B,0 +; CALL LP_TAB_H2 + + EX AF,AF' + EXX +; JR LP_END_P +; program save registers +; for low printer + +LP_END_P: + EX AF,AF' + EXX + RLA + LD E,A + LD (WIN_MAP_IX+WIN_HL),HL ; место печати + LD (WIN_MAP_IX+WIN_DE),DE + LD (WIN_MAP_IX+WIN_BC),BC + + LP_CLOSE_PG + EXX + EX AF,AF' + AND A + RET + +LP_END_P2: + EX AF,AF' + EXX + LP_CLOSE_PG + EXX + EX AF,AF' + RET + +;******************************************************** + + +LP_OPEN_S: + LD A,B + ADD A,A + CP LP_SC_TAB_SIZE + CCF + RET C + PUSH HL + LD HL,LP_SC_TAB + + ADD A,L + LD L,A + LD A,H + ADC A,0 + LD H,A + LD A,(HL) + INC HL + LD H,(HL) + LD L,A + EX (SP),HL + RET + +LP_SC_TAB: + + DW LP_SET_32 + DW LP_SET_64 + DW LP_SET_40 + DW LP_SET_80 + DW LP_SET_32X + DW LP_SET_64X + DW LP_SET_40X + DW LP_SET_80X + DW PIC_SET_S1 + DW PIC_SET_S2 +; DW LP_SET_MNU + +LP_SC_TAB_SIZE EQU $-LP_SC_TAB + +;LP_SET_MNU: +; SCF +; RET +; LD IX,LP_SCR_MNU +; JR LP_SET_MODE + +LP_SET_32: + LD HL,4104H +LP_SET_32X: + LD IX,LP_SCR_32 + JR LP_SET_MODE + +LP_SET_64: + LD HL,4104H +LP_SET_64X: + LD IX,LP_SCR_64 + JR LP_SET_MODE + +LP_SET_40: + LD HL,4000H +LP_SET_40X: + LD IX,LP_SCR_40 + JR LP_SET_MODE + +LP_SET_80: + LD HL,4000H +LP_SET_80X: + LD IX,LP_SCR_80 + JR LP_SET_MODE + +PIC_SET_S1: + LD IX,PIC_320X256_1 + JR LP_SET_MODE + +PIC_SET_S2: + LD IX,PIC_320X256_2 + JR LP_SET_MODE + +;********************************************************** + + +WIN_OPEN_WIN: ; HL - место на экране по знакоместам + ; IX - описатель окна + LP_OPEN_PG + LD (WIN_MAP_SC),IX ; карта окна + PUSH HL + PUSH DE + LD HL,(WIN_MAP_SC) + LD DE,WIN_MAP_IX + LD BC,32 + LDIR + LD IX,WIN_MAP_IX + POP DE + POP HL + + LD L,(IX+WIN_PLACE_H) + LD H,(IX+WIN_PLACE_V) + LD (IX+WIN_MODE_E),E + JR WIN_OPEN_W1 + + +PIC_FN0: + +LP_SET_MODE: ; HL - PLACE + ; IX - SIZE_HOR; IX+1 - SIZE_VER; IX+2,3; IX+4 - MODE; + ; E - страница моды. + LD A,H + AND 10H ; переместить бит 4 в регистр E + XOR E + LD E,A + + LD A,L + AND 3FH + ADD HL,HL + ADD HL,HL + LD L,A + RES 7,H + RES 6,H +; LD A,H +; AND 3FH +; LD H,A + +;LP_SET_MODE_M: + + LP_OPEN_PG + LD (WIN_MAP_SC),IX ; карта окна + PUSH HL + PUSH DE + LD HL,(WIN_MAP_SC) + LD DE,WIN_MAP_IX + LD BC,32 + LDIR + LD IX,WIN_MAP_IX + POP DE + POP HL + + LD (IX+WIN_PLACE_H),L + LD (IX+WIN_PLACE_V),H + LD (IX+WIN_MODE_E),E + +WIN_OPEN_W1: + LD A,L + ADD A,A + INC A ; вычисление PORT_Y + BIT 4,E + JR NZ,LP_SET_NO_OR + OR 80H ; если вывод на второй экран +LP_SET_NO_OR: + LD (IX+WIN_H_BEG),A + LD D,A + LD A,(IX+WIN_SIZE_H) + ADD A,A + ADD A,D + LD (IX+WIN_H_END),A + + LD (WIN_MODE_SC),DE ; место по горизонтали и страница моды + + LD A,H + AND 3FH + ADD A,A + ADD A,A + LD L,A + LD H,0C3H + LD (WIN_MODE_SH),HL ; место по вертикали с адресом + LD (IX+WIN_V_BEG),A + LD A,(IX+WIN_SIZE_V) + ADD A,A + ADD A,A + ADD A,L + LD (IX+WIN_V_END),A + + LD L,(IX+WIN_V_BEG) + LD H,0C3H + LD B,(IX+WIN_SIZE_V) ; размер по вертикали + + + LD (IX+WIN_WORK_1),0 + LD A,(IX+WIN_MODE) ; знакогенератор плюс режим + LD (IX+WIN_WORK_2),A + BIT 4,A + JR NZ,LP_SET_LOOP ; переход, если текстовый режим + + AND 0F0H + LD C,A + LD A,(IX+WIN_GR_X) + RRCA + RRCA + RRCA + LD D,A + AND 0FH + OR C + LD (IX+WIN_WORK_2),A + + LD A,D + AND 0E0H + LD C,A + LD A,(IX+WIN_GR_Y) + AND 1FH + OR C + RLCA + RLCA + RLCA + LD (IX+WIN_WORK_1),A + +LP_SET_LOOP: + LD D,(IX+WIN_H_BEG) ; D - начало строки (PORT_Y) + LD C,(IX+WIN_SIZE_H) ; размер по горизонтали + LD A,(IX+WIN_MODE) ; знакогенератор плюс режим + + PUSH BC + CALL LP_MODE_LINE + POP BC + INC L + INC L + INC L + INC L + DJNZ LP_SET_LOOP + + LD A,(IX+WIN_MODE_E) + AND 1 + OUT (RGMOD),A +; LD A,(IX+WIN_MODE) +; BIT 4,A + + LP_CLOSE_PG + +; RET Z + + CALL LP_INI_P + +; LD A,(WIN_MODE_SC) +; OUT (RGMOD),A + + CALL LP_SIZE +; AND A + XOR A + RET + +WIN_CLOSE_WIN: + SCF + RET + + +LP_MODE_LINE: ; Установка режима на линии + BIT 0,(IX+WIN_MODE_S) + JP NZ,LP_MODE_LINE2 ; идти на спектрум + BIT 4,A + JP Z,LP_MODE_LINE3 ; идти на графику! + DEC D + +; текстовый режим. + + EX AF,AF' + LD A,50H + OUT (PAGE3),A + LD A,L ; вертикальное положение + CP 80H + JR NC,LP_EXIT_MODE + +LP_MODE_RECURSE: + LD A,D ; горизонтальное положение + AND 7FH + SUB 80 + JR C,LP_MODE_LL + SUB 48 + NEG + + LD E,A ; запомнить + ADD A,D + LD D,A ; новое положение + + LD A,E ; восстановить + RRA + AND 3FH + SUB C + JR NC,LP_EXIT_MODE + NEG + LD C,A ; новое значение C + JR LP_MODE_RECURSE + +LP_MODE_LL: + NEG + RRA + AND 3FH + CP C + JR NC,LP_MODE_LR + LD C,A +LP_MODE_LR: + EX AF,AF' + + INC D +LP_MD_LL1: + EX AF,AF' + LD A,D + OUT (RGADR),A + EX AF,AF' + LD (HL),A ; режим + INC D + EX AF,AF' + LD A,D + OUT (RGADR),A + EX AF,AF' + LD (HL),A ; режим + INC D + DEC C + JR NZ,LP_MD_LL1 + + EX AF,AF' +LP_EXIT_MODE: + LD A,SYS_PAGE + OUT (PAGE3),A + EX AF,AF' + + RET + + +LP_MODE_LINE2: ; Установка режима на линии + +; Spectrum mode + + LD A,(IX+WIN_WORK_2) + LD B,(IX+WIN_WORK_1) + + DEC D + + EX AF,AF' + LD A,50H + OUT (PAGE3),A + LD A,L ; вертикальное положение + CP 80H + JR NC,LP_EXIT_MODE2 + +LP_MODE_RECURSE2: + LD A,D ; горизонтальное положение + AND 7FH + SUB 80 + JR C,LP_MODE_LL2 + SUB 48 + NEG + + LD E,A ; запомнить + ADD A,D + LD D,A ; новое положение + + LD A,E ; восстановить + RRA + AND 3FH + SUB C + JR NC,LP_EXIT_MODE2 + NEG + LD C,A ; новое значение C + JR LP_MODE_RECURSE2 + +LP_MODE_LL2: + NEG + RRA + AND 3FH + CP C + JR NC,LP_MODE_LR2 + LD C,A +LP_MODE_LR2: + EX AF,AF' + + INC D +LP_MD_LL2: + EX AF,AF' + LD A,D + OUT (RGADR),A + EX AF,AF' + LD (HL),A ; режим + INC L + LD (HL),B + INC L + LD (HL),B + DEC L + DEC L + + INC D + BIT 4,A + JR NZ,lp_md_ll3x + INC B +lp_md_ll3x: + EX AF,AF' + LD A,D + OUT (RGADR),A + EX AF,AF' + LD (HL),A ; режим + INC L + LD (HL),B + INC L + LD (HL),B + DEC L + DEC L + + INC D + + INC B + JR NZ,LP_NO_ADD_40 + ADD A,40H +LP_NO_ADD_40: + + DEC C + JR NZ,LP_MD_LL2 + + EX AF,AF' +LP_EXIT_MODE2: + LD A,SYS_PAGE + OUT (PAGE3),A + EX AF,AF' + + LD (IX+WIN_WORK_1),B + LD (IX+WIN_WORK_2),A + + RET + + +LP_MODE_LINE3: ; Установка режима на линии + +; Grafic Mode + + LD A,(IX+WIN_WORK_1) + LD B,(IX+WIN_WORK_2) + + DEC D + + EX AF,AF' + LD A,50H + OUT (PAGE3),A + LD A,L ; вертикальное положение + CP 80H + JR NC,LP_EXIT_MODE3 + +LP_MODE_RECURSE3: + LD A,D ; горизонтальное положение + AND 7FH + SUB 80 + JR C,LP_MODE_LL3 + SUB 48 + NEG + + LD E,A ; запомнить + ADD A,D + LD D,A ; новое положение + + LD A,E ; восстановить + RRA + AND 3FH + SUB C + JR NC,LP_EXIT_MODE3 + NEG + LD C,A ; новое значение C + JR LP_MODE_RECURSE3 + +LP_MODE_LL3: + NEG + RRA + AND 3FH + CP C + JR NC,LP_MODE_LR3 + LD C,A +LP_MODE_LR3: + EX AF,AF' + + INC D + + BIT 5,B + JR Z,LP_GR_640 + +LP_MD_LL3: + + EX AF,AF' + LD A,D + OUT (RGADR),A + EX AF,AF' + LD (HL),B + INC L + LD (HL),a + INC L + LD (HL),0 + + DEC L + DEC L + INC D + INC D + INC A + LD E,A + AND 7 + JR NZ,LP_NO_INC_B + LD A,E + SUB 8 + LD E,A + INC B +LP_NO_INC_B: + LD A,E + + DEC C + JR NZ,LP_MD_LL3 + +LP_640_RET: + EX AF,AF' +LP_EXIT_MODE3: + LD A,SYS_PAGE + OUT (PAGE3),A + EX AF,AF' + + LD A,(IX+WIN_WORK_1) + ADD A,8 + LD (IX+WIN_WORK_1),A + + RET + +LP_GR_640: + + EX AF,AF' + LD A,D + OUT (RGADR),A + EX AF,AF' + LD (HL),B ; режим + INC L + LD (HL),A ; режим 2 + INC L + LD (HL),0 + INC D + EX AF,AF' + LD A,D + OUT (RGADR),A + EX AF,AF' + LD (HL),0 + DEC L + LD (HL),A + DEC L + LD (HL),B ; режим + INC D + INC A + LD E,A + AND 7 + JR NZ,LP_NO_INC_B6 + LD A,E + SUB 8 + LD E,A + INC B +LP_NO_INC_B6: + LD A,E + + DEC C + JR NZ,LP_GR_640 + JP LP_640_RET + + +;***************************************************************** + + +LP_SCROLL_UD: + DEC B + JR Z,LP_SCROLL_UP + DEC B + JR Z,LP_SCROLL_DN +; DEC B +; JR Z,LP_SCROLL_UP2 +; DEC B +; JR Z,LP_SCROLL_DN2 + SCF + RET + + +LP_SCROLL_UP: ; Вход : D - верхняя строка ( от 0 ) +; ; E - число скроллируемых строк + DEC E + RET Z + + LP_OPEN_PG + LD B,E + LD E,0 + PUSH BC + CALL LP_AT_D + CALL LP_TAB_E ; вычислить переменные для строки + POP BC + +LP_SCROLL_L2: + + EXX + PUSH HL + EXX + POP HL + + LD E,L + LD D,H + INC L + INC L + INC L + INC L + + LD A,(WIN_MAP_IX+WIN_V_END) + CP L + JR C,LP_SCROLL_U_EXIT ; если конец, то выход + + + PUSH BC + + LD A,L + EXX + LD L,A + EXX + + LD A,(WIN_MAP_IX+WIN_SIZE_H) + + ADD A,A ; число байт в строке для переноса + ADD A,A + + LD C,A + LD B,0 + + LD A,50H + OUT (PAGE3),A ; открыть страницу для переноса + EXX + LD A,D ; получить порт начала строки + EXX + + +LP_SCROLL_L1: + OUT (RGADR),A + INC A ; следующее значение порта + LDI + LDD + OUT (RGADR),A + INC A ; следующее значение порта + LDI + LDD + JP PE,LP_SCROLL_L1 + + LD A,SYS_PAGE + OUT (PAGE3),A + + POP BC + + DJNZ LP_SCROLL_L2 + +LP_SCROLL_U_EXIT: + + LP_CLOSE_PG + RET + +LP_SCROLL_DN: ; Вход : D - верхняя строка ( от 0 ) +; ; E - число скроллируемых строк + DEC E + RET Z + + LP_OPEN_PG + LD B,E + LD E,0 + LD A,D + ADD A,B +; DEC A + LD D,A + PUSH BC + CALL LP_AT_D + CALL LP_TAB_E + POP BC + + +LP_SCROLL_D2: + + + EXX + PUSH HL + EXX + POP HL + + LD E,L + LD D,H + DEC L + DEC L + DEC L + DEC L + + LD A,(WIN_MAP_IX+WIN_V_BEG) + CP L + JR Z,LP_SCROLL_U_CONT + JR NC,LP_SCROLL_U_EXIT ; если конец, то выход + +LP_SCROLL_U_CONT: + + PUSH BC + + LD A,L + EXX + LD L,A + EXX + + LD A,(WIN_MAP_IX+WIN_SIZE_H) + ADD A,A + ADD A,A + LD C,A + LD B,0 + + LD A,50H + OUT (PAGE3),A + EXX + LD A,D + EXX + + +LP_SCROLL_D1: + OUT (RGADR),A + INC A + LDI + LDD + OUT (RGADR),A + INC A + LDI + LDD + JP PE,LP_SCROLL_D1 + + LD A,SYS_PAGE + OUT (PAGE3),A + + POP BC + DJNZ LP_SCROLL_D2 + +LP_SCROLL_D_EXIT: + + LP_CLOSE_PG + RET + + + +WIN_GET_SYM: ; DE - место символа в окне + ; А - номер окна + ; выход: HL - символ/атрибут B - знакогенератор + AND A + SCF + RET NZ + + CALL LP_BEG_P + + CALL LP_AT_D + CALL LP_TAB_E + + LD A,50H + OUT (PAGE3),A + + EXX + + LD A,D + OUT (RGADR),A + + LD A,(HL) + EXX + LD L,A + EXX + INC L + LD A,(HL) + EXX + LD H,A + EXX + DEC L + DEC L + LD A,(HL) + EXX + LD B,A + EXX + INC L + + EXX + + LD A,SYS_PAGE + OUT (PAGE3),A + + CALL LP_END_P + RET + + +WIN_PUT_SYM: ; DE - место символа в окне + ; А - номер окна + ; HL - символ/атрибут B - знакогенератор + AND A + SCF + RET NZ + + CALL LP_BEG_P + + CALL LP_AT_D + CALL LP_TAB_E + + LD A,50H + OUT (PAGE3),A + + EXX + + LD A,D + OUT (RGADR),A + + EXX + LD A,L + EXX + + LD (HL),A + INC L + + EXX + LD A,H + EXX + + LD (HL),A + DEC L + DEC L + + EXX + LD A,B + EXX + + LD (HL),A + INC L + + EXX + + LD A,SYS_PAGE + OUT (PAGE3),A + + CALL LP_END_P + RET + +WIN_MOVE_WIN: ; HL - размер окна + ; DE - положение окна + AND A ; A - номер окна + SCF ; IX - новое положение окна + RET NZ + PUSH IX + PUSH HL + LD B,0FFH + LD IX,0C000H + CALL WIN_COPY_WIN1 + POP HL + POP DE + LD B,0FFH + LD IX,0C000H + CALL WIN_REST_WIN1 + AND A + RET + + +WIN_COPY_WIN: ; HL - размер сохраняемого окна + ; DE - место сохраняемого окна + ; IX - адрес сохраняемого окна, в странице 4000h + ; А - номер окна, B - страница + AND A + SCF + RET NZ + +WIN_COPY_WIN1: + CALL LP_BEG_P + + IN A,(PAGE1) + PUSH AF + + CALL LP_AT_D + CALL LP_TAB_E + + LD (SYS_WORK1),SP + + LD A,B + OUT (PAGE3),A + LD A,50H + OUT (PAGE1),A + + EXX + RES 7,H + LD C,D + EXX + +LP_COPY_L2: + + LD A,L + EXX + LD B,A + + ADD A,A + DB 0DDH + ADD A,L + DB 0DDH + LD L,A + JR NC,LP_NO_INC_IX1 + DB 0DDH + INC H +LP_NO_INC_IX1: + + LD SP,IX + + LD A,C + ADD A,B + +LP_COPY_L1: + DEC A + OUT (RGADR),A + LD E,(HL) + INC L + LD D,(HL) + DEC L + PUSH DE + DJNZ LP_COPY_L1 + + INC HL + INC HL + INC HL + INC HL + + EXX + + DEC H + JR NZ,LP_COPY_L2 + + LD A,SYS_PAGE + OUT (PAGE3),A + LD SP,(SYS_WORK1) + + POP AF + OUT (PAGE1),A + + CALL LP_END_P2 + AND A + RET + + +WIN_REST_WIN: ; HL - размер сохраняемого окна + ; DE - место сохраняемого окна + ; IX - адрес данных восстанавливаемого окна + AND A + SCF + RET NZ +WIN_REST_WIN1: + CALL LP_BEG_P + + IN A,(PAGE1) + PUSH AF + + CALL LP_AT_D + CALL LP_TAB_E + + LD (SYS_WORK1),SP + LD SP,IX + + LD A,B + OUT (PAGE3),A + LD A,50H + OUT (PAGE1),A + + EXX + RES 7,H + LD C,D + EXX + +LP_REST_L2: + + LD A,L + EXX + LD B,A + + LD A,C + +LP_REST_L1: + + OUT (RGADR),A + INC A + POP DE + LD (HL),E + INC L + LD (HL),D + DEC L + + DJNZ LP_REST_L1 + + INC HL + INC HL + INC HL + INC HL + + EXX + DEC H + + JR NZ,LP_REST_L2 + + LD A,SYS_PAGE + OUT (PAGE3),A + + LD SP,(SYS_WORK1) + + POP AF + OUT (PAGE1),A + + CALL LP_END_P2 + AND A + RET + + + diff --git a/ZX_EXP/EXP_PIC2.ASM b/ZX_EXP/EXP_PIC2.ASM new file mode 100644 index 0000000..7257f7b --- /dev/null +++ b/ZX_EXP/EXP_PIC2.ASM @@ -0,0 +1,525 @@ + +;****************************************************************** +PIC_FN1: ; вход: dE - вертикаль, HL - горизонталь, + ; B - цвет точки, A - экран + AND A + SCF + RET NZ + + IN A,(RGADR) + PUSH AF + + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + + LD A,(WIN_MAP_IX+WIN_GRAF_Y) + ADD A,E + OUT (RGADR),A + LD A,B + LD BC,(WIN_MAP_IX+WIN_GRAF_X) + ADD HL,BC + LD B,A + LD A,50H + OUT (PAGE3),A + LD (HL),A + EX AF,AF' + OUT (PAGE3),A + + POP AF + OUT (RGADR),A + RET + + +PIC_FN2: ; вход: dE - вертикаль, HL - горизонталь. + ; IX - длина линии в точках не 0 !!! + ; FILL - by A + LD D,A ; ???? + IN A,(PAGE1) + EX AF,AF' + + LD A,B + ADD A,50H + OUT (PAGE1),A + + BIT 0,B + LD BC,40H+04000H + JR Z,PIC_FN2_NO_2ND + LD BC,40H+320+04000H +PIC_FN2_NO_2ND: + ADD HL,BC + + LD A,E + OUT (RGADR),A + + DB 0DDH + LD A,H + AND A + JR Z,PIC_FN2_NO256 +PIC_FN2_256L: + LD B,64 +PIC_FN2_256: + LD (HL),D + INC HL + LD (HL),D + INC HL + LD (HL),D + INC HL + LD (HL),D + INC HL + DJNZ PIC_FN2_256 + DEC A + JR NZ,PIC_FN2_256L + +PIC_FN2_NO256: + DB 0DDH + LD B,L + AND A + RR B + JR NC,PIC_FN2_NO1 + LD (HL),D + INC HL + AND A +PIC_FN2_NO1: + RR B + JR NC,PIC_FN2_NO2 + LD (HL),D + INC HL + LD (HL),D + INC HL + AND A +PIC_FN2_NO2: + XOR A + CP B + JR Z,PIC_FN2_NO4 +PIC_FN2_4: + LD (HL),D + INC HL + LD (HL),D + INC HL + LD (HL),D + INC HL + LD (HL),D + INC HL + DJNZ PIC_FN2_4 +PIC_FN2_NO4: + + EX AF,AF' + OUT (PAGE1),A + XOR A + OUT (RGADR),A + RET + +;************************************************************************ +PIC_FN3: ; вход: dE - вертикаль, HL - горизонталь. + ; A',HL' - адрес данных во второй странице ! + ; BC' - длина данных + ; ВЫВОД ЛИНИИ ТОЧЕК + IN A,(PAGE3) + LD C,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,C + LD (COPY_PAGE3),A + IN A,(PAGE2) + LD (COPY_PAGE2),A + IN A,(PAGE1) + LD (COPY_PAGE1),A + + LD A,B + ADD A,50H + OUT (PAGE1),A + + BIT 0,B + LD BC,40H+04000H + JR Z,PIC_FN3_NO_2ND + LD BC,40H+320+04000H +PIC_FN3_NO_2ND: + ADD HL,BC + + LD A,E + OUT (RGADR),A + + LD A,H + EXX + LD D,A + EXX + LD A,L + EXX + LD E,A + EXX + + EX AF,AF' + OUT (PAGE2),A + EX AF,AF' + LD HL,RAMD_FAT + LD L,A + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(HL) + OUT (PAGE3),A + EXX + LDIR + BIT 6,H + JR Z,PIC_FN3_NO + RES 6,H + EX AF,AF' +PIC_FN3_NO: + EXX + + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(COPY_PAGE1) + OUT (PAGE1),A + LD A,(COPY_PAGE2) + OUT (PAGE2),A + LD A,(COPY_PAGE3) + OUT (PAGE3),A + + XOR A + OUT (RGADR),A + RET + +;************************************************************************** +PIC_FN4: ; вход: HL - данные палитры RGB, E - начальный цвет + ; D - число цветов, B - PAL-mask, A - page PAL + + PUSH IX + + EX AF,AF' + IN A,(RGADR) + PUSH AF ; сохранить положение RGADR + LD A,E + OUT (RGADR),A ; начальный цвет в порт + LD A,D ; сохранить число цветов + EX AF,AF' ; вернуть страницу PAL + + BIT 7,H ; проверить адрес данных + LD C,PAGE3 ; если ниже 8000h + LD D,0C3H ; то граф.экран на странице 3 + JR Z,PIC_FN4_NO_PAGE1 + LD D,043H ; иначе + LD C,PAGE1 ; на странице 1 +PIC_FN4_NO_PAGE1: + + ADD A,A + ;JR C,PIC_PAL_READ + + ADD A,A + AND 1CH + XOR 0E0H ; младший байт адреса PAL + LD E,A ; DE - адрес в видео-ОЗУ + PUSH DE + POP IX ; адрес палитры + + LD D,B ; маска PAL + EX AF,AF' ; вернуть число цветов + LD B,A ; число цветов + + IN E,(C) ; сохранить страницу 3 + LD A,50H ; страница графического RAM + OUT (C),A + +PIC_FN4_L1: ; цикл установки цветов палитры + + LD A,(HL) ; B + AND D + LD (IX+2),A + INC HL + + LD A,(HL) ; G + AND D + LD (IX+1),A + INC HL + + LD A,(HL) ; R + AND D + LD (IX+0),A + INC HL + + LD A,(HL) ; Y + AND D + LD (IX+3),A + INC HL + + IN A,(RGADR) + INC A + OUT (RGADR),A + + DJNZ PIC_FN4_L1 ; выполнять, пока не 0 + + OUT (C),E ; восстановить страницу 3 + + POP AF + OUT (RGADR),A ; восстановить Y_PORT + POP IX + ;AND A + RET + +; PIC_PAL_READ: + +; ADD A,A +; AND 1CH +; XOR 0E0H ; младший байт адреса PAL +; LD E,A ; DE - адрес в видео-ОЗУ +; PUSH DE +; POP IX ; адрес палитры + +; LD D,B ; маска PAL +; EX AF,AF' ; вернуть число цветов +; LD B,A ; число цветов + +; IN E,(C) ; сохранить страницу 3 +; LD A,50H ; страница графического RAM +; OUT (C),A + +; PIC_FN4_L2: ; цикл установки цветов палитры + +; LD A,(IX+2) +; LD (HL),A ; B +; INC HL + +; LD A,(IX+1) +; LD (HL),A ; G +; INC HL + +; LD A,(IX+0) +; LD (HL),A ; R +; INC HL + +; LD A,(IX+3) +; LD (HL),A ; Y +; INC HL + +; IN A,(RGADR) +; INC A +; OUT (RGADR),A + +; DJNZ PIC_FN4_L2 ; выполнять, пока не 0 + +; OUT (C),E ; восстановить страницу 3 + +; POP AF +; OUT (RGADR),A ; восстановить Y_PORT +; POP IX +; AND A +; RET + + +;******************************************************************** +PIC_FN5: ; УСТАНОВКА СТРАНИЦЫ MODE + LD A,E + AND 1 + OUT (RGMOD),A + RET +;******************************************************************* +PIC_FN6: ; A - page_pal, E - номер палитры, B - тип палитры + + LD D,A + DEC B + JP Z,SET_PAL_GRAF + DEC B + JP Z,SET_PAL_ZX + ; DEC B + ; JP Z,SET_PAL_IBM + +; LD IX,PAL_DAT1 +; JR Z,PIC_FN6_L1 +; DEC B +; LD IX,PAL_DAT +; JR Z,PIC_FN6_L1 + + SCF + RET + +;PIC_FN6_L1: +; AND 3 +; ADD A,A +; ADD A,A +; ADD A,0E0H +; LD L,A +; LD H,43H +; IN A,(PAGE1) +; PUSH AF +; +; LD A,50H +; OUT (PAGE1),A +; XOR A +; OUT (RGADR),A +; EX AF,AF' +; call PAL_LOOP_M +; +; POP AF +; OUT (PAGE1),A +; XOR A +; OUT (RGADR),A +; +; RET + +;****************************************************************** +PIC_FN7: ; Рисование линии. dЕ - вертикаль, HL - горизонталь + ; H' - высота, C'/DE',B' - add_parameters + ; A - color + LD D,A + IN A,(PAGE1) + PUSH AF + + LD A,B + ADD A,50H + OUT (PAGE1),A + + BIT 0,B + LD BC,40H+04000H + JR Z,PIC_FN7_NO_2ND + LD BC,40H+320+04000H +PIC_FN7_NO_2ND: + ADD HL,BC + + LD A,E +PIC_FN7_L1: + OUT (RGADR),A ; страница видео-ОЗУ + LD (HL),D + + EXX + EX AF,AF' + + DEC H + JR Z,PIC_FN7_EXIT + + LD A,C ; NEXT HORIZONTAL + ADD A,B + LD C,A + LD A,E + EXX + ADC A,L + LD L,A + EXX + LD A,D + EXX + ADC A,H + LD H,A + + INC E ; NEXT VERTICAL + + EX AF,AF' + INC A + JR PIC_FN7_L1 + +PIC_FN7_EXIT: + EXX + POP AF + OUT (PAGE1),A + XOR A + OUT (RGADR),A + RET + +;****************************************************************** +PIC_FN8: ; Рисование линии. dЕ - вертикаль, HL - горизонталь + ; H' - высота, C'/DE',B' - add_parameters + ; (IX) - color + IN A,(PAGE1) + PUSH AF + + LD A,B + ADD A,50H + OUT (PAGE1),A + + BIT 0,B + LD BC,40H+04000H + JR Z,PIC_FN8_NO_2ND + LD BC,40H+320+04000H +PIC_FN8_NO_2ND: + ADD HL,BC + + LD A,E + +PIC_FN8_L1: + OUT (RGADR),A ; страница видео-ОЗУ + LD D,(IX) + INC IX + LD (HL),D + EXX + EX AF,AF' + + DEC H + JR Z,PIC_FN8_EXIT + + LD A,C ; NEXT HORIZONTAL + ADD A,B + LD C,A + LD A,E + EXX + ADC A,L + LD L,A + EXX + LD A,D + EXX + ADC A,H + LD H,A + + INC E ; NEXT VERTICAL + EX AF,AF' + INC A + JR PIC_FN8_L1 + +PIC_FN8_EXIT: + EXX + POP AF + OUT (PAGE1),A + XOR A + OUT (RGADR),A + RET + +;************************************************** +; Вывод спрайта на экран +PIC_FN9: ; HL - горизоталь, dE - вертикаль + ; A',HL' - адрес данных +;********************************* + +PIC_FN10: ; HL,E - координаты D - color + ; B - Magnify +PIC_FN11: + +PIC_FN12: + +PIC_FN13: + +PIC_FN14: + +PIC_FN15: + SCF + RET + +;************************************ +; +; Экранные таблицы + +; LP_SCR_80: +; DB 40,32,0,0,1BH,0,0,0 +; DB 0,0,0,0,0,0,0,0 +; ; +; LP_SCR_40: +; DB 40,32,0,0,7BH,0,0,0 +; DB 0,0,0,0,0,0,0,0 + +; LP_SCR_32: +; DB 32,24,4,4,30H,1,0,0 +; DB 0,0,0,0,0,0,0,0 + +; LP_SCR_64: +; DB 32,24,4,4,9BH,0,0,0 +; DB 0,0,0,0,0,0,0,0 + +; PIC_320X256_1: +; DB 40,32,0,0,20H,0,8,0 +; DB 0,0,0,0,0,0,0,0 +; ; +; PIC_320X256_2: +; DB 40,32,0,0,60H,0,48,0 +; DB 0,0,0,0,0,0,0,0 + +; ;************************************ +; + + diff --git a/ZX_EXP/EXP_SCR.ASM b/ZX_EXP/EXP_SCR.ASM new file mode 100644 index 0000000..7bbe1f7 --- /dev/null +++ b/ZX_EXP/EXP_SCR.ASM @@ -0,0 +1,289 @@ +;***************************************************** +; +; Экранные таблицы +; Таблицы расположения данных !!! +; +;***************************************************** + +; byte 1 +; 0..4 - VIDEO_PAGE +; 5 - mode 42/84 ; 0 - 42,1 - 84 +; 6 - next line +; 7 - end table +; byte 2 - number of places +; byte 3..4 - adress places/ +; 4000-5FFF - Spectrum modes/ C000-FFFF - Graf mode +; +LP_S MACRO PG,XX,ADR + DEFB PG,XX + DEFW ADR + ENDM +;********************************* +; BYTE 0 - бит 7 - конец строки, остальное - режим экрана +; BYTE 1 - число строк +; BYTE 2,3 - адрес линии + +PIC_S MACRO pg,cnt,adr + DB pg + DB cnt + DW adr + ENDM + + + ALIGN 8 +LP_SCR_MNU: +; +; LP_S 040h,14,040E7H +; +; LP_S 040h,14,04807H +; LP_S 040h,14,04827H +; LP_S 040h,14,04847H +; LP_S 040h,14,04867H +; LP_S 040h,14,04887H +; LP_S 040h,14,048A7H +; LP_S 040h,14,048C7H +; LP_S 040h,14,048E7H +; +; DEFB 0FFh,0,0,0 + +LP_SCR_80: + DB 40,32,0,0,1BH,0,0,0 + DB 0,0,0,0,0,0,0,0 + +; LP_S 01100010B,40,4000H +; LP_S 01100010B,40,4028H +; LP_S 01100010B,40,4050H +; LP_S 01100010B,40,4078H +; LP_S 01100010B,40,40A0H +; LP_S 01100010B,40,40C8H +; +; LP_S 01100010B,40,4800H +; LP_S 01100010B,40,4828H +; LP_S 01100010B,40,4850H +; LP_S 01100010B,40,4878H +; LP_S 01100010B,40,48A0H +; LP_S 01100010B,40,48C8H +; +; LP_S 01100010B,40,5000H +; LP_S 01100010B,40,5028H +; LP_S 01100010B,40,5050H +; LP_S 01100010B,40,5078H +; LP_S 01100010B,40,50A0H +; LP_S 01100010B,40,50C8H +; +; LP_S 01100100B,40,4000H +; LP_S 01100100B,40,4028H +; LP_S 01100100B,40,4050H +; LP_S 01100100B,40,4078H +; LP_S 01100100B,40,40A0H +; LP_S 01100100B,40,40C8H +; +; LP_S 01100100B,40,4800H +; LP_S 01100100B,40,4828H +; LP_S 01100100B,40,4850H +; LP_S 01100100B,40,4878H +; LP_S 01100100B,40,48A0H +; LP_S 01100100B,40,48C8H +; +; LP_S 01100100B,40,5000H +; LP_S 01100100B,40,5028H +; +; DEFB 0FFh,0,0,0 +; +LP_SCR_40: + DB 40,32,0,0,7BH,0,0,0 + DB 0,0,0,0,0,0,0,0 +; +; LP_S 01000100B,40,5050H +; LP_S 01000100B,40,5078H +; LP_S 01000100B,40,50A0H +; LP_S 01000100B,40,50C8H +; +; LP_S 01000110B,40,4000H +; LP_S 01000110B,40,4028H +; LP_S 01000110B,40,4050H +; LP_S 01000110B,40,4078H +; LP_S 01000110B,40,40A0H +; LP_S 01000110B,40,40C8H +; +; LP_S 01000110B,40,4800H +; LP_S 01000110B,40,4828H +; LP_S 01000110B,40,4850H +; LP_S 01000110B,40,4878H +; LP_S 01000110B,40,48A0H +; LP_S 01000110B,40,48C8H +; +; LP_S 01000110B,40,5000H +; LP_S 01000110B,40,5028H +; LP_S 01000110B,40,5050H +; LP_S 01000110B,40,5078H +; LP_S 01000110B,40,50A0H +; LP_S 01000110B,40,50C8H +; +; LP_S 01001000B,40,4000H +; LP_S 01001000B,40,4028H +; LP_S 01001000B,40,4050H +; LP_S 01001000B,40,4078H +; LP_S 01001000B,40,40A0H +; LP_S 01001000B,40,40C8H +; +; LP_S 01001000B,40,4800H +; LP_S 01001000B,40,4828H +; LP_S 01001000B,40,4850H +; LP_S 01001000B,40,4878H +; +; DEFB 0FFh,0,0,0 +; +LP_SCR_32: + DB 32,24,4,4,30H,1,0,0 + DB 0,0,0,0,0,0,0,0 +; +; LP_S 040h,32,04000H +; LP_S 040h,32,04020H +; LP_S 040h,32,04040H +; LP_S 040h,32,04060H +; LP_S 040h,32,04080H +; LP_S 040h,32,040A0H +; LP_S 040h,32,040C0H +; LP_S 040h,32,040E0H +; +; LP_S 040h,32,04800H +; LP_S 040h,32,04820H +; LP_S 040h,32,04840H +; LP_S 040h,32,04860H +; LP_S 040h,32,04880H +; LP_S 040h,32,048A0H +; LP_S 040h,32,048C0H +; LP_S 040h,32,048E0H +; +; LP_S 040h,32,05000H +; LP_S 040h,32,05020H +; LP_S 040h,32,05040H +; LP_S 040h,32,05060H +; LP_S 040h,32,05080H +; LP_S 040h,32,050A0H +; LP_S 040h,32,050C0H +; LP_S 040h,32,050E0H +; +; LP_S 0FFh,0,0 +; +LP_SCR_64: + DB 32,24,4,4,9BH,0,0,0 + DB 0,0,0,0,0,0,0,0 +; +; LP_S 06Ah,32,04000H +; LP_S 06Ah,32,04020H +; LP_S 06Ah,32,04040H +; LP_S 06Ah,32,04060H +; LP_S 06Ah,32,04080H +; LP_S 06Ah,32,040A0H +; LP_S 06Ah,32,040C0H +; LP_S 06Ah,32,040E0H +; +; LP_S 06Ah,32,04800H +; LP_S 06Ah,32,04820H +; LP_S 06Ah,32,04840H +; LP_S 06Ah,32,04860H +; LP_S 06Ah,32,04880H +; LP_S 06Ah,32,048A0H +; LP_S 06Ah,32,048C0H +; LP_S 06Ah,32,048E0H +; +; LP_S 06Ah,32,05000H +; LP_S 06Ah,32,05020H +; LP_S 06Ah,32,05040H +; LP_S 06Ah,32,05060H +; LP_S 06Ah,32,05080H +; LP_S 06Ah,32,050A0H +; LP_S 06Ah,32,050C0H +; LP_S 06Ah,32,050E0H +; +; LP_S 0FFh,0,0 + +PIC_320X256_1: + DB 40,32,0,0,20H,0,8,0 + DB 0,0,0,0,0,0,0,0 + +; +; PIC_S 020H,40,4040H +; PIC_S 028H,40,4040H +; PIC_S 020H,40,4440H +; PIC_S 028H,40,4440H +; PIC_S 020H,40,4840H +; PIC_S 028H,40,4840H +; PIC_S 020H,40,4C40H +; PIC_S 028H,40,4C40H +; +; PIC_S 020H,40,5040H +; PIC_S 028H,40,5040H +; PIC_S 020H,40,5440H +; PIC_S 028H,40,5440H +; PIC_S 020H,40,5840H +; PIC_S 028H,40,5840H +; PIC_S 020H,40,5C40H +; PIC_S 028H,40,5C40H +; +; PIC_S 020H,40,6040H +; PIC_S 028H,40,6040H +; PIC_S 020H,40,6440H +; PIC_S 028H,40,6440H +; PIC_S 020H,40,6840H +; PIC_S 028H,40,6840H +; PIC_S 020H,40,6C40H +; PIC_S 028H,40,6C40H +; +; PIC_S 020H,40,7040H +; PIC_S 028H,40,7040H +; PIC_S 020H,40,7440H +; PIC_S 028H,40,7440H +; PIC_S 020H,40,7840H +; PIC_S 028H,40,7840H +; PIC_S 020H,40,7C40H +; PIC_S 028H,40,7C40H +; +; PIC_S 0FFH,0,0 +; +PIC_320X256_2: + DB 40,32,0,0,60H,0,48,0 + DB 0,0,0,0,0,0,0,0 + +; PIC_S 060H,40,4180H +; PIC_S 068H,40,4180H +; PIC_S 060H,40,4580H +; PIC_S 068H,40,4580H +; PIC_S 060H,40,4980H +; PIC_S 068H,40,4980H +; PIC_S 060H,40,4D80H +; PIC_S 068H,40,4D80H +; +; PIC_S 060H,40,5180H +; PIC_S 068H,40,5180H +; PIC_S 060H,40,5580H +; PIC_S 068H,40,5580H +; PIC_S 060H,40,5980H +; PIC_S 068H,40,5980H +; PIC_S 060H,40,5D80H +; PIC_S 068H,40,5D80H +; +; PIC_S 060H,40,6180H +; PIC_S 068H,40,6180H +; PIC_S 060H,40,6580H +; PIC_S 068H,40,6580H +; PIC_S 060H,40,6980H +; PIC_S 068H,40,6980H +; PIC_S 060H,40,6D80H +; PIC_S 068H,40,6D80H +; +; PIC_S 060H,40,7180H +; PIC_S 068H,40,7180H +; PIC_S 060H,40,7580H +; PIC_S 068H,40,7580H +; PIC_S 060H,40,7980H +; PIC_S 068H,40,7980H +; PIC_S 060H,40,7D80H +; PIC_S 068H,40,7D80H +; +; PIC_S 0FFH,0,0 +; +; +; diff --git a/ZX_EXP/FLEX.ASM b/ZX_EXP/FLEX.ASM new file mode 100644 index 0000000..b887dcb --- /dev/null +++ b/ZX_EXP/FLEX.ASM @@ -0,0 +1,679 @@ + +SYCLES EQU 0 +START EQU 1 +ST1 EQU 2 +DPAL1 EQU 3 +ST2 EQU 4 +DPAL2 EQU 5 + +NEXT_LIN_H MACRO + EX AF,AF' + INC A + OUT (RGADR),A + EX AF,AF' + ENDM + +NEXT_LIN_HP MACRO + local MD_NO_INC_H + EX AF,AF' + INC A + OUT (RGADR),A + JR NZ,MD_NO_INC_H + inc l +MD_NO_INC_H: + EX AF,AF' + ENDM + + +;***************************************** +; +; Инициализация страниц для +; спектрумовского режима и +; восстановление спектрумовского +; экрана +; +; Возврат по IX +; +; !!! NO USE STACK !!! +; +;***************************************** + +INIT_PAGES: + + AND A + LD E,A + EX AF,AF' + LD A,E + +; BIT 7,A +; JR Z,SCORPION_256_MODE +; BIT 6,A +; JR Z,PENTAGON_128_MODE +; +;PENTAGON_48_MODE: +; AND 03CH +; OUT (PAGE0),A +; INC A +; OUT (PAGE1),A +; INC A +; OUT (PAGE2),A +; INC A +; LD D,A +; LD BC,1FFDH +; XOR A +; OUT (C),A +; LD B,7FH +; OUT (C),A +; LD A,D +; OUT (PAGE3),A +; JR INIT_VIDEO_REG +; +;PENTAGON_128_MODE: +; LD D,8 +; AND 038H +; JR ALL_MODE + +SCORPION_256_MODE: + LD D,16 + AND 030H ; до 4-х спектрумов +ALL_MODE: + + OUT (PAGE0),A + XOR 5 + OUT (PAGE1),A + XOR 7 + OUT (PAGE2),A + OR 0FH + LD E,A + +D_P2: + DEC D + + LD A,D + RLCA + AND 10H + LD BC,01FFDH + OUT (C),A + + LD A,D + AND 7 + OR 40H + LD B,7FH + OUT (C),A + + LD A,E + OUT (PAGE3),A + DEC E + + DEC D + INC D + JR NZ,D_P2 + +INIT_VIDEO_REG: + + XOR A + OUT (RGADR),A ; регистр видео адреса +; OUT (RGSCR),A ; регистр экрана + OUT (RGMOD),A ; регистр моды + + LD HL,04000H + LD DE,04000H + LD BC,01B00H + LDIR ; Restore old Spectrum Screen + + EX AF,AF' + BIT 6,A + JR NZ,NO_SCREEN_ALT + + EX AF,AF' + + LD A,7 ; седьмая экранная страница + LD BC,7FFDH + OUT (C),A + + LD HL,0C000H + LD DE,0C000H + LD BC,01B00H + LDIR ; Restore old Spectrum Screen + + XOR A + LD BC,7FFDH + OUT (C),A + + EX AF,AF' + +NO_SCREEN_ALT: + AND A + JP (IX) + +;**************************************** +; Загрузка режима экрана. +;**************************************** + +; CALL SINC_DEF +; CALL SET_PAL_ZX +; RET + +;**************************************** +;**************************************** +;**************************************** +;**************************************** + +CL0 EQU 200 +CL1 EQU 240 + +; SET_PAL_IBM: +; LD DE,08000h +; JR SET_PAL_ +SET_PAL_ZX: +;SET_PAL_: + PUSH IX + IN A,(RGADR) + PUSH AF + IN A,(PAGE3) + EX AF,AF' + LD A,50H + OUT (PAGE3),A + LD DE,0 + LD IX,0C3F0H +SET_PAL_ZX1: + CALL GENERATE_PAL1 + LD A,E + OUT (RGADR),A + LD (IX),L + LD (IX+1),B + LD (IX+2),C + LD (IX+3),H + INC E + JR NZ,SET_PAL_ZX1 + LD BC,4 + ADD IX,BC + INC D + LD A,D + CP 04 + JR NZ,SET_PAL_ZX1 + + EX AF,AF' + OUT (PAGE3),A + POP AF + OUT (RGADR),A + POP IX + RET + +; генерация спектрумовской палитры. + +GENERATE_PAL1: ; E - атрибут, D - номер PAL +; ; ВЫХОД - C - BLUE, L - RED, B - GREEN, H - INTENSITY +; + XOR A + LD C,A + LD B,A + LD L,A + + ; BIT 7,D + ; JR NZ,GENERATE_IBM + + BIT 1,D + JR NZ,GEM_PAL_FLH +GEM_PAL_NOF: + BIT 0,D + JR Z,GEN_PAL_PAP + +GEN_PAL_INK: + LD A,CL0 + BIT 6,E + JR Z,GEN_PAL_NOI1 + LD A,CL1 +GEN_PAL_NOI1: + BIT 0,E ; BLUE + JR Z,GEN_PP_NO1 + LD C,A +GEN_PP_NO1: + BIT 1,E ; RED + JR Z,GEN_PP_NO2 + LD L,A +GEN_PP_NO2: + BIT 2,E ; GREEN + JR Z,GEN_PP_NO3 + LD B,A +GEN_PP_NO3: + JR GEN_PP_NO6 + +GEM_PAL_FLH: + BIT 7,E + JR Z,GEM_PAL_NOF + BIT 0,D + JR Z,GEN_PAL_INK + +GEN_PAL_PAP: + LD A,CL0 + BIT 6,E + JR Z,GEN_PAL_NOI2 + LD A,CL1 +GEN_PAL_NOI2: + BIT 3,E ; BLUE + JR Z,GEN_PP_NO4 + LD C,A +GEN_PP_NO4: + BIT 4,E ; RED + JR Z,GEN_PP_NO5 + LD L,A +GEN_PP_NO5: + BIT 5,E ; GREEN + JR Z,GEN_PP_NO6 + LD B,A +GEN_PP_NO6: + + LD A,C + AND A + RRA + ADD A,L + RRA + ADD A,B + RRA + LD H,A ; B/W mode + + RET + +;**************************************** + +; GENERATE_IBM: + +; BIT 7,E +; JR Z,GEN_IBM_NO_FLH +; BIT 1,D +; JR Z,GEN_IBM_PAPER + +; GEN_IBM_NO_FLH: +; BIT 0,D +; JR Z,GEN_IBM_PAPER + +; GEN_IBM_INK: + +; GEN_NO_INTENS: +; LD A,0A8h +; GEN_INTENS: +; BIT 0,E ; BLUE +; JR Z,GEN_PPI_NO4 +; LD C,A +; GEN_PPI_NO4: +; BIT 2,E ; RED +; JR Z,GEN_PPI_NO5 +; LD L,A +; GEN_PPI_NO5: +; BIT 1,E ; GREEN +; JR Z,GEN_PPI_NO6 +; LD B,A +; GEN_PPI_NO6: + +; LD A,E +; AND 0Fh +; CP 6 +; JR NZ,no_correct +; LD B,54h +; no_correct: + +; BIT 3,E +; JR Z,GEN_PP_NO6 + +; LD A,54h +; ADD A,C +; LD C,A +; LD A,54h +; ADD A,B +; LD B,A +; LD A,54h +; ADD A,L +; LD L,A + +; JR GEN_PP_NO6 + +; GEN_IBM_PAPER: +; LD A,0A8h +; BIT 4,E ; BLUE +; JR Z,GEN_PPI_NO4X +; LD C,A +; GEN_PPI_NO4X: +; BIT 6,E ; RED +; JR Z,GEN_PPI_NO5X +; LD L,A +; GEN_PPI_NO5X: +; BIT 5,E ; GREEN +; JR Z,GEN_PPI_NO6X +; LD B,A +; GEN_PPI_NO6X: + +; LD A,E +; AND 070h +; CP 60h +; JR NZ,no_correct2 +; LD B,54h +; no_correct2: + +; JR GEN_PP_NO6 + +;**************************************** +;**************************************** +;**************************************** + +SET_PAL_GRAF: ; D - номер графической палитры + PUSH IX + IN A,(RGADR) + PUSH AF + + IN A,(PAGE3) + EX AF,AF' + LD A,50H + OUT (PAGE3),A + + + LD IX,0C3E0H + LD A,D + AND 3 + ADD A,A + ADD A,A + LD E,A + LD D,0 + ADD IX,DE + + LD E,0 + + XOR A + LD B,A + LD C,A + LD L,A + LD H,A + +SET_PAL_GR1: + LD A,E + OUT (RGADR),A + + LD (IX),L + LD (IX+1),B + LD (IX+2),C + LD (IX+3),H + + CALL GENERATE_PAL3 + + INC E + LD A,E + CP 40 + JR NZ,SET_PAL_GR1 + + XOR A + LD B,A + LD C,A + LD L,A + LD H,A + +SET_PAL_GR2: + LD A,E + OUT (RGADR),A + + LD (IX),L + LD (IX+1),B + LD (IX+2),C + LD (IX+3),H + + CALL GENERATE_PAL2 + + INC E + JR NZ,SET_PAL_GR2 + + EX AF,AF' + OUT (PAGE3),A + POP AF + OUT (RGADR),A + POP IX + RET +; +;;********************************* +; + +GENERATE_PAL3: ; gray-scale + LD A,B + ADD A,6 + LD B,A + LD C,A + LD L,A + + LD A,C + AND A + RRA + ADD A,L + RRA + ADD A,B + RRA + LD H,A + + RET + +GENERATE_PAL2: ; C - BLUE; L - RED; B - GREEN; H - INTENSITY + + LD A,C + ADD A,50 + LD C,A + JR NC,GEN_PAL2_L1 + LD C,0 + LD A,L + ADD A,50 + LD L,A + JR NC,GEN_PAL2_L1 + LD L,0 + LD A,B + ADD A,50 + LD B,A + JR NC,GEN_PAL2_L1 + LD B,0 + +GEN_PAL2_L1: + LD A,C + AND A + RR A + ADD A,L + RR A + ADD A,B + RR A + LD H,A + + RET +; +;;***************************************** + + +SINC_DEF: + LD C,PAGE3 + IN B,(C) + LD A,SYS_PAGE + OUT (C),A + LD HL,(CONFIG_ALL) + OUT (C),B + LD DE,L_PENTAGON + AND A + SBC HL,DE + JR Z,PENT_SINC + LD IX,L_SCORPION + ; AND A + ; SBC HL,DE + ; JR Z,SCOR_SINC +; ORIG_SINC: +; LD IX,L_ORIGINAL +; JR PROG_SCR +SCOR_SINC: + ;LD IX,L_SCORPION + JR PROG_SCR +PENT_SINC: + LD IX,L_PENTAGON + JR PROG_SCR +FN_SINC: + AND A + JR Z,SINC_DEF + DEC A + LD IX,L_SCORPION + JR Z,PROG_SCR + DEC A + LD IX,L_PENTAGON + JR Z,PROG_SCR + ; DEC A + ; JR Z,ORIG_SINC + SCF + RET + +PROG_SCR: + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + EX AF,AF' + LD (COPY_PAGE3),A + IN A,(RGADR) + LD (COPY_RGADR),A + + LD (CONFIG_ALL),IX + + LD A,0 +MM_LOOP7: + OUT (RGADR),A + EX AF,AF' + + LD A,#50 + OUT (PAGE3),A + + LD HL,#C300 +MM_LOOP6: + LD C,(IX) +MM_LOOP4: + LD E,(IX+1) + LD D,(IX+2) +MM_LOOP5: + LD A,(DE) + INC DE + AND A + JR Z,MM_EXIT_1 + LD B,A + LD A,(DE) + INC DE +MM_LOOP3: + + LD (HL),A + INC L + LD (HL),0 + INC L + LD (HL),0 + + EX AF,AF' + INC A + OUT (RGADR),A + EX AF,AF' + + LD (HL),0 + DEC L + LD (HL),0 + DEC L + LD (HL),A + + EX AF,AF' + INC A + OUT (RGADR),A + EX AF,AF' + + DJNZ MM_LOOP3 + JR MM_LOOP5 + +MM_EXIT_1: + INC HL ; next line + INC HL + INC HL + INC HL + IN A,(RGADR) + AND 80H + OUT (RGADR),A + DEC C + JR NZ,MM_LOOP4 + INC IX + INC IX + INC IX + LD A,(IX) + AND A + JR NZ,MM_LOOP6 + + LD A,SYS_PAGE + OUT (PAGE3),A + LD IX,(CONFIG_ALL) + + EX AF,AF' + ADD A,80H + JR NC,MM_LOOP7 + + LD A,(COPY_RGADR) + OUT (RGADR),A + LD A,(COPY_PAGE3) + OUT (PAGE3),A + + RET + +;L_SCR: DB 41,38H,3,3CH,4,3CH,7,3CH,9,38H,0 +;L_INT: DB 42,3CH,1,3DH,5,3CH,7,3CH,9,3CH,0 +;L_INT2: DB 41,38H,1,3DH,6,3CH,7,3CH,9,38H,0 +;L_BLN: DB 41,3CH,3,3CH,4,3CH,7,3CH,9,3CH,0 +;L_SNC: DB 41,3CH,3,3CH,4,3CH,7,3CH,9,3CH,0 +;L_RES: DB 41,38H,3,3EH,4,3EH,7,3EH,9,38H,0 + +L_SCR: DB 41,0F8H,3,0FCH,4,0FCH,7,0FCH,9,0F8H,0 +L_INT: DB 40,0FCH,2,0FDH,6,0FCH,7,0FCH,9,0FCH,0 +L_INT2: DB 41,0F8H,1,0FDH,6,0FCH,7,0FCH,9,0F8H,0 +L_BLN: DB 41,0FCH,3,0FCH,4,0FCH,7,0FCH,9,0FCH,0 +L_SNC: DB 41,0FCH,3,0FCH,4,0FCH,7,0FCH,9,0FCH,0 +L_RES: DB 41,0F8H,3,0FEH,4,0FEH,7,0FEH,9,0F8H,0 + +L_PENTAGON: + DB 33 + DW L_SCR + DB 1 +; DW L_BLN + DW L_INT + DB 3 + DW L_SNC + DB 1 + DW L_BLN + DB 1 + DW L_SCR + DB 1 + DW L_RES + DB 0 + +L_SCORPION: + DB 31 + DW L_SCR + DB 1 + DW L_INT2 + DB 1 + DW L_SCR + DB 1 + DW L_SNC + DB 3 + DW L_SNC + DB 1 + DW L_BLN + DB 2 + DW L_RES + DB 0 + +; L_ORIGINAL: +; DB 33 +; DW L_SCR +; DB 1 +; DW L_SNC +; DB 1 +; DW L_INT +; DB 2 +; DW L_SNC +; DB 1 +; DW L_BLN +; DB 1 +; DW L_SCR +; DB 1 +; DW L_RES + DB 0 + +FLEX_END: diff --git a/ZX_EXP/FONT.ASM b/ZX_EXP/FONT.ASM new file mode 100644 index 0000000..bde6d66 --- /dev/null +++ b/ZX_EXP/FONT.ASM @@ -0,0 +1,151 @@ +ZG_ADRESS: + DB 000h,07Eh,07Eh,06Ch,010h,038h,010h,000h,0FFh,000h,0FFh,00Fh,03Ch,03Fh,07Fh,018h + DB 080h,002h,018h,066h,07Fh,03Eh,000h,018h,018h,018h,000h,000h,000h,000h,000h,000h + DB 000h,030h,06Ch,06Ch,030h,000h,038h,060h,018h,060h,000h,000h,000h,000h,000h,006h + DB 07Ch,030h,078h,078h,01Ch,0FCh,038h,0FCh,078h,078h,000h,000h,018h,000h,060h,078h + DB 07Ch,030h,0FCh,03Ch,0F8h,0FEh,0FEh,03Ch,0CCh,078h,01Eh,0E6h,0F0h,0C6h,0C6h,038h + DB 0FCh,078h,0FCh,078h,0FCh,0CCh,0CCh,0C6h,0C6h,0CCh,0FEh,078h,0C0h,078h,010h,000h + DB 030h,000h,0E0h,000h,01Ch,000h,038h,000h,0E0h,030h,00Ch,0E0h,070h,000h,000h,000h + DB 000h,000h,000h,000h,010h,000h,000h,000h,000h,000h,000h,01Ch,018h,0E0h,076h,000h + DB 00Eh,0FCh,0F8h,0FCh,07Eh,0FCh,0DBh,03Ch,0C6h,0D6h,0C6h,006h,0C6h,0C6h,07Ch,0FEh + DB 0FCh,07Ch,0FCh,0C6h,018h,0C3h,0CCh,0C6h,0D6h,0D6h,0F0h,0C2h,0C0h,07Ch,0CEh,07Eh + DB 000h,004h,000h,000h,000h,000h,000h,000h,000h,030h,000h,000h,000h,000h,000h,000h + DB 022h,055h,0DBh,010h,010h,010h,014h,000h,000h,014h,014h,000h,014h,014h,010h,000h + DB 010h,010h,000h,010h,000h,010h,010h,014h,014h,000h,014h,000h,014h,000h,014h,010h + DB 014h,000h,000h,014h,010h,000h,000h,014h,010h,010h,000h,0FFh,000h,0F0h,00Fh,0FFh + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 048h,048h,060h,018h,008h,010h,030h,000h,060h,000h,000h,01Fh,0A0h,060h,000h,000h + DB 000h,081h,0FFh,0FEh,038h,07Ch,010h,000h,0FFh,03Ch,0C3h,007h,066h,033h,063h,0DBh + DB 0E0h,00Eh,03Ch,066h,0DBh,063h,000h,03Ch,03Ch,018h,018h,030h,000h,024h,018h,0FFh + DB 000h,078h,06Ch,06Ch,07Ch,0C6h,06Ch,060h,030h,030h,066h,030h,000h,000h,000h,00Ch + DB 0C6h,070h,0CCh,0CCh,03Ch,0C0h,060h,0CCh,0CCh,0CCh,030h,030h,030h,000h,030h,0CCh + DB 0C6h,078h,066h,066h,06Ch,062h,062h,066h,0CCh,030h,00Ch,066h,060h,0EEh,0E6h,06Ch + DB 066h,0CCh,066h,0CCh,0B4h,0CCh,0CCh,0C6h,0C6h,0CCh,0C6h,060h,060h,018h,038h,000h + DB 030h,000h,060h,000h,00Ch,000h,06Ch,000h,060h,000h,000h,060h,030h,000h,000h,000h + DB 000h,000h,000h,000h,030h,000h,000h,000h,000h,000h,000h,030h,018h,030h,0DCh,010h + DB 01Eh,0C0h,0CCh,0C0h,066h,0C0h,0DBh,066h,0C6h,0C6h,0CCh,00Eh,0EEh,0C6h,0C6h,0C6h + DB 0C6h,0C6h,030h,0C6h,07Eh,066h,0CCh,0C6h,0D6h,0D6h,030h,0C2h,0C0h,0C6h,0DBh,0C6h + DB 000h,078h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 088h,0AAh,077h,010h,010h,010h,014h,000h,000h,014h,014h,000h,014h,014h,010h,000h + DB 010h,010h,000h,010h,000h,010h,010h,014h,014h,000h,014h,000h,014h,000h,014h,010h + DB 014h,000h,000h,014h,010h,000h,000h,014h,010h,010h,000h,0FFh,000h,0F0h,00Fh,0FFh + DB 000h,000h,000h,000h,018h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 0FCh,000h,030h,030h,014h,010h,030h,000h,090h,000h,000h,010h,0D0h,090h,000h,000h + DB 000h,0A5h,0DBh,0FEh,07Ch,038h,038h,018h,0E7h,066h,099h,00Fh,066h,03Fh,07Fh,03Ch + DB 0F8h,03Eh,07Eh,066h,0DBh,038h,000h,07Eh,07Eh,018h,00Ch,060h,0C0h,066h,03Ch,0FFh + DB 000h,078h,06Ch,0FEh,0C0h,0CCh,038h,0C0h,060h,018h,03Ch,030h,000h,000h,000h,018h + DB 0CEh,030h,00Ch,00Ch,06Ch,0F8h,0C0h,00Ch,0CCh,0CCh,030h,030h,060h,0FCh,018h,00Ch + DB 0DEh,0CCh,066h,0C0h,066h,068h,068h,0C0h,0CCh,030h,00Ch,06Ch,060h,0FEh,0F6h,0C6h + DB 066h,0CCh,066h,060h,030h,0CCh,0CCh,0C6h,06Ch,0CCh,08Ch,060h,030h,018h,06Ch,000h + DB 018h,078h,060h,078h,00Ch,078h,060h,076h,06Ch,070h,00Ch,066h,030h,0CCh,0F8h,078h + DB 0DCh,076h,0DCh,07Ch,07Ch,0CCh,0CCh,0C6h,0C6h,0CCh,0FCh,030h,018h,030h,000h,038h + DB 036h,0C0h,0CCh,0C0h,066h,0C0h,07Eh,006h,0CEh,0CEh,0D8h,01Eh,0FEh,0C6h,0C6h,0C6h + DB 0C6h,0C0h,030h,0C6h,0DBh,03Ch,0CCh,0C6h,0D6h,0D6h,030h,0C2h,0C0h,006h,0DBh,0C6h + DB 078h,0C0h,0F8h,0FCh,07Eh,078h,0DBh,078h,0CCh,0CCh,0CCh,00Eh,0C6h,0CCh,078h,0FCh + DB 022h,055h,0DBh,010h,010h,0F0h,014h,000h,0F0h,0F4h,014h,0FCh,0F4h,014h,0F0h,000h + DB 010h,010h,000h,010h,000h,010h,01Fh,014h,017h,01Fh,0F7h,0FFh,017h,0FFh,0F7h,0FFh + DB 014h,0FFh,000h,014h,01Fh,01Fh,000h,014h,0FFh,010h,000h,0FFh,000h,0F0h,00Fh,0FFh + DB 0F8h,078h,0FCh,0CCh,07Eh,0C6h,0CCh,0CCh,0D6h,0D6h,0F0h,0C2h,0C0h,07Ch,0CEh,07Ch + DB 0C0h,078h,018h,060h,010h,010h,000h,064h,090h,000h,000h,010h,090h,020h,03Ch,000h + DB 000h,081h,0FFh,0FEh,0FEh,0FEh,07Ch,03Ch,0C3h,042h,0BDh,07Dh,066h,030h,063h,0E7h + DB 0FEh,0FEh,018h,066h,07Bh,06Ch,000h,018h,018h,018h,0FEh,0FEh,0C0h,0FFh,07Eh,07Eh + DB 000h,030h,000h,06Ch,078h,018h,076h,000h,060h,018h,0FFh,0FCh,000h,0FCh,000h,030h + DB 0DEh,030h,038h,038h,0CCh,00Ch,0F8h,018h,078h,07Ch,000h,000h,0C0h,000h,00Ch,018h + DB 0DEh,0CCh,07Ch,0C0h,066h,078h,078h,0C0h,0FCh,030h,00Ch,078h,060h,0FEh,0DEh,0C6h + DB 07Ch,0CCh,07Ch,030h,030h,0CCh,0CCh,0D6h,038h,078h,018h,060h,018h,018h,0C6h,000h + DB 000h,00Ch,07Ch,0CCh,07Ch,0CCh,0F0h,0CCh,076h,030h,00Ch,06Ch,030h,0FEh,0CCh,0CCh + DB 066h,0CCh,076h,0C0h,030h,0CCh,0CCh,0D6h,06Ch,0CCh,098h,0E0h,000h,01Ch,000h,06Ch + DB 066h,0FCh,0FCh,0C0h,066h,0F8h,018h,03Ch,0DEh,0DEh,0F8h,036h,0D6h,0FEh,0C6h,0C6h + DB 0FCh,0C0h,030h,07Eh,0DBh,018h,0CCh,07Eh,0D6h,0D6h,03Eh,0F2h,0FCh,01Eh,0FBh,07Eh + DB 00Ch,0F8h,0CCh,0C0h,066h,0CCh,07Eh,0CCh,0CCh,0CCh,0D8h,01Eh,0EEh,0CCh,0CCh,0CCh + DB 088h,0AAh,0EEh,010h,010h,010h,014h,000h,010h,004h,014h,004h,004h,014h,010h,000h + DB 010h,010h,000h,010h,000h,010h,010h,014h,010h,010h,000h,000h,010h,000h,000h,000h + DB 014h,000h,000h,014h,010h,010h,000h,014h,010h,010h,000h,0FFh,000h,0F0h,00Fh,0FFh + DB 0CCh,0CCh,030h,0CCh,0DBh,06Ch,0CCh,0CCh,0D6h,0D6h,030h,0C2h,0C0h,0C6h,0DBh,0CCh + DB 0F8h,0CCh,030h,030h,010h,010h,0FCh,098h,060h,030h,000h,090h,090h,040h,03Ch,000h + DB 000h,0BDh,0C3h,07Ch,07Ch,0FEh,0FEh,03Ch,0C3h,042h,0BDh,0CCh,03Ch,030h,063h,0E7h + DB 0F8h,03Eh,018h,066h,01Bh,06Ch,07Eh,07Eh,018h,07Eh,00Ch,060h,0C0h,066h,0FFh,03Ch + DB 000h,030h,000h,0FEh,00Ch,030h,0DCh,000h,060h,018h,03Ch,030h,000h,000h,000h,060h + DB 0F6h,030h,060h,00Ch,0FEh,00Ch,0CCh,030h,0CCh,00Ch,000h,000h,060h,000h,018h,030h + DB 0DEh,0FCh,066h,0C0h,066h,068h,068h,0CEh,0CCh,030h,0CCh,06Ch,062h,0D6h,0CEh,0C6h + DB 060h,0DCh,06Ch,018h,030h,0CCh,0CCh,0FEh,038h,030h,032h,060h,00Ch,018h,000h,000h + DB 000h,07Ch,066h,0C0h,0CCh,0FCh,060h,0CCh,066h,030h,00Ch,078h,030h,0FEh,0CCh,0CCh + DB 066h,0CCh,066h,078h,030h,0CCh,0CCh,0FEh,038h,0CCh,030h,030h,018h,030h,000h,0C6h + DB 0FEh,0C6h,0C6h,0C0h,066h,0C0h,07Eh,006h,0F6h,0F6h,0CCh,066h,0C6h,0C6h,0C6h,0C6h + DB 0C0h,0C0h,030h,006h,0DBh,03Ch,0CCh,006h,0D6h,0D6h,033h,0DAh,0C6h,006h,0DBh,036h + DB 07Ch,0CCh,0F8h,0C0h,066h,0FCh,018h,018h,0DCh,0DCh,0F0h,036h,0D6h,0FCh,0CCh,0CCh + DB 022h,055h,0DBh,010h,0F0h,0F0h,0F4h,0FCh,0F0h,0F4h,014h,0F4h,0FCh,0FCh,0F0h,0F0h + DB 01Fh,0FFh,0FFh,01Fh,0FFh,0FFh,01Fh,017h,01Fh,017h,0FFh,0F7h,017h,0FFh,0F7h,0FFh + DB 0FFh,0FFh,0FFh,01Fh,01Fh,01Fh,01Fh,0FFh,0FFh,0F0h,01Fh,0FFh,0FFh,0F0h,00Fh,000h + DB 0CCh,0C0h,030h,07Ch,0DBh,038h,0CCh,07Ch,0D6h,0D6h,03Eh,0F2h,0F8h,01Eh,0FBh,07Ch + DB 0C0h,0FCh,060h,018h,010h,010h,000h,000h,000h,030h,030h,050h,090h,0F0h,03Ch,000h + DB 000h,099h,0E7h,038h,038h,0D6h,07Ch,018h,0E7h,066h,099h,0CCh,018h,070h,067h,03Ch + DB 0E0h,00Eh,07Eh,000h,01Bh,038h,07Eh,03Ch,018h,03Ch,018h,030h,0FEh,024h,0FFh,018h + DB 000h,000h,000h,06Ch,0F8h,066h,0CCh,000h,030h,030h,066h,030h,030h,000h,030h,0C0h + DB 0E6h,030h,0CCh,0CCh,00Ch,0CCh,0CCh,030h,0CCh,018h,030h,030h,030h,0FCh,030h,000h + DB 0C0h,0CCh,066h,066h,06Ch,062h,060h,066h,0CCh,030h,0CCh,066h,066h,0C6h,0C6h,06Ch + DB 060h,078h,066h,0CCh,030h,0CCh,078h,0EEh,06Ch,030h,066h,060h,006h,018h,000h,000h + DB 000h,0CCh,066h,0CCh,0CCh,0C0h,060h,07Ch,066h,030h,0CCh,06Ch,030h,0D6h,0CCh,0CCh + DB 07Ch,07Ch,060h,00Ch,034h,0CCh,078h,0FEh,06Ch,07Ch,064h,030h,018h,030h,000h,0C6h + DB 0C6h,0C6h,0C6h,0C0h,066h,0C0h,0DBh,0C6h,0E6h,0E6h,0C6h,0C6h,0C6h,0C6h,0C6h,0C6h + DB 0C0h,0C6h,030h,0C6h,07Eh,066h,0CCh,006h,0D6h,0D6h,033h,0DAh,0C6h,0C6h,0DBh,066h + DB 0CCh,0CCh,0C6h,0C0h,066h,0C0h,07Eh,0CCh,0ECh,0ECh,0CCh,066h,0C6h,0CCh,0CCh,0CCh + DB 088h,0AAh,077h,010h,010h,010h,014h,014h,010h,014h,014h,014h,000h,000h,000h,010h + DB 000h,000h,010h,010h,000h,010h,010h,014h,000h,014h,000h,014h,014h,000h,014h,000h + DB 000h,010h,014h,000h,000h,010h,014h,014h,010h,000h,010h,0FFh,0FFh,0F0h,00Fh,000h + DB 0F8h,0CCh,030h,00Ch,07Eh,06Ch,0CCh,00Ch,0D6h,0D6h,033h,0DAh,0CCh,0C6h,0DBh,06Ch + DB 0C0h,0C0h,000h,000h,010h,050h,030h,064h,000h,000h,000h,030h,000h,000h,03Ch,000h + DB 000h,081h,0FFh,010h,010h,010h,010h,000h,0FFh,03Ch,0C3h,0CCh,07Eh,0F0h,0E6h,0DBh + DB 080h,002h,03Ch,066h,01Bh,0CCh,07Eh,018h,018h,018h,000h,000h,000h,000h,000h,000h + DB 000h,030h,000h,06Ch,030h,0C6h,076h,000h,018h,060h,000h,000h,030h,000h,030h,080h + DB 07Ch,0FCh,0FCh,078h,01Eh,078h,078h,030h,078h,070h,030h,030h,018h,000h,060h,030h + DB 078h,0CCh,0FCh,03Ch,0F8h,0FEh,0F0h,03Eh,0CCh,078h,078h,0E6h,0FEh,0C6h,0C6h,038h + DB 0F0h,01Ch,0E6h,078h,078h,0FCh,030h,0C6h,0C6h,078h,0FEh,078h,002h,078h,000h,000h + DB 000h,076h,0DCh,078h,076h,078h,0F0h,00Ch,0E6h,078h,0CCh,0E6h,078h,0C6h,0CCh,078h + DB 060h,00Ch,0F0h,0F8h,018h,076h,030h,06Ch,0C6h,00Ch,0FCh,01Ch,018h,0E0h,000h,0FEh + DB 0C6h,0FCh,0FCh,0C0h,0FFh,0FEh,0DBh,07Ch,0C6h,0C6h,0C6h,0C6h,0C6h,0C6h,07Ch,0C6h + DB 0C0h,07Ch,030h,07Ch,018h,0C3h,0FEh,006h,0FEh,0FFh,03Eh,0F2h,0FCh,07Ch,0CEh,0C6h + DB 07Eh,078h,0FCh,0C0h,0FFh,07Ch,0DBh,078h,0CCh,0CCh,0CCh,0C6h,0C6h,0CCh,078h,0CCh + DB 022h,055h,0DBh,010h,010h,010h,014h,014h,010h,014h,014h,014h,000h,000h,000h,010h + DB 000h,000h,010h,010h,000h,010h,010h,014h,000h,014h,000h,014h,014h,000h,014h,000h + DB 000h,010h,014h,000h,000h,010h,014h,014h,010h,000h,010h,0FFh,0FFh,0F0h,00Fh,000h + DB 0C0h,078h,030h,0CCh,018h,0C6h,0FEh,00Ch,0FEh,0FFh,03Eh,0F2h,0F8h,07Ch,0CEh,0CCh + DB 0FEh,07Ch,078h,078h,010h,020h,030h,098h,000h,000h,000h,010h,000h,000h,000h,000h + DB 000h,07Eh,07Eh,000h,000h,038h,038h,000h,0FFh,000h,0FFh,078h,018h,0E0h,0C0h,018h + DB 000h,000h,018h,000h,000h,078h,000h,0FFh,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,060h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,060h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0FFh + DB 000h,000h,000h,000h,000h,000h,000h,0F8h,000h,000h,078h,000h,000h,000h,000h,000h + DB 0F0h,01Eh,000h,000h,000h,000h,000h,000h,000h,0F8h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,0C3h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,006h,000h,000h,003h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,0C3h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 088h,0AAh,0EEh,010h,010h,010h,014h,014h,010h,014h,014h,014h,000h,000h,000h,010h + DB 000h,000h,010h,010h,000h,010h,010h,014h,000h,014h,000h,014h,014h,000h,014h,000h + DB 000h,010h,014h,000h,000h,010h,014h,014h,010h,000h,010h,0FFh,0FFh,0F0h,00Fh,000h + DB 0C0h,000h,000h,078h,018h,000h,006h,000h,000h,003h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,010h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZX_EXP/SERVICE.ASM b/ZX_EXP/SERVICE.ASM new file mode 100644 index 0000000..74c38c6 --- /dev/null +++ b/ZX_EXP/SERVICE.ASM @@ -0,0 +1,789 @@ +;.PRINTX "Service." + +SERVICE: + LD HL,SR_TAB + LD DE,SR_MENU + LD BC,SR_LEN + PUSH HL + CALL RUN_MENU ;09f3 + POP HL + CALL EXEC_PNT + JR SERVICE + +SR_TAB: + DW UTILIT + DW SR_48R + DW SR_48 + DW TURBO_OFF_1 + DW TURBO_ON_1 + DW CLEAR_RAM + DW RET_FROM_M + +; DC - every last character of a string will have bit 7 set +SR_MENU: + BYTE 8 ; количество пунктов + BYTE 'Options ',#FF ; заголовок меню + DC 'RUN disk.trd' + DC '128 TR-DOS' + DC '48 TR-DOS' + DC 'TURBO OFF ' + DC 'TURBO ON ' + DC 'Clear RAM' + DC 'RETURN' + DC ' ' ; маркер конца + +SR_LEN EQU $-SR_MENU + +;***************************** + +SR_48R: + LD A,10H + JR SR_48A +;***************************** + +SR_48: + LD A,30H +SR_48A: + LD BC,7FFDH + OUT (C),A ; Стопор на PORT_128 !!! + LD BC,0 ; НА 0 !! + PUSH BC + LD BC,3D2Fh ; В DOS ! + PUSH BC + JP SW_ROM + +;******************************* + IFDEF ZX_BIOS +TURBO_OFF_1: + LD A,BIOS.FN_TURBO.OFF + LD C,BIOS.FN_TURBO + JP ToBIOS_18 +TURBO_ON_1: + LD A,BIOS.FN_TURBO.ON + LD C,BIOS.FN_TURBO + JP ToBIOS_18 + + ELSE + ; +TURBO_OFF_1: + DI + CALL DOS_ON + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(CONFIG_DE) + AND 0FCH + OR 2 + LD (CONFIG_DE),A + OUT (CNF_PORT),A + EX AF,AF' + OUT (PAGE3),A + CALL DOS_OFF + EI + RET +TURBO_ON_1: + DI + CALL DOS_ON + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD A,(CONFIG_DE) + AND 0FCH + OR 3 + LD (CONFIG_DE),A + OUT (CNF_PORT),A + EX AF,AF' + OUT (PAGE3),A + EI + CALL DOS_OFF + RET + + ENDIF +;******************************* + +CLEAR_RAM: + DI + LD C,9Fh + RST 18h + IFDEF ZX_BIOS + ; при очистке памяти из меню спектрума поломается возврат в DSS по CAD, поэтому отключаем возврат + LD A,#EE ;!HARDCODE Conf_port.RET_PORT + LD BC,0*256 + #F8 ;!HARDCODE BIOS.SET_PORTS + CALL #3D13 ;!HARDCODE ToBIOS_3D13 + ENDIF + EI + RET + +;SR_CL_MEM: +; DI +; LD A,10H +;SR_CL_R2: +; LD BC,1FFDH +; OUT (C),A +; EX AF,AF' +; XOR A +;SR_CL_R1: +; LD BC,7FFDH +; OUT (C),A +; LD HL,0C000H +; LD DE,0C001H +; LD BC,03FFFH +; LD (HL),L +; LDIR +; INC A +; CP 48H +; JR NZ,SR_CL_R1 +; EX AF,AF' +; CP 0 +; JP Z,BASIC_128 ; выход на сброс BASIC 128 +; LD A,0 +; JR SR_CL_R2 + +;***************************** +; *** MENU IS-DOS *** +;***************************** + +ISDOS: + LD HL,IS_TAB + LD DE,IS_MENU + LD BC,IS_MENU.Size + PUSH HL + CALL RUN_MENU + POP HL + CALL EXEC_PNT + JR ISDOS + +IS_TAB: +; DW TR_DOS +; DW SP_DOS + DW SPRINTER_1X + DW AY8910_X + DW PENTAGON + DW SCORPION + DW PENTAGON512 + DW SPRINTER_2X + DW RET_FROM_M + +; DC - every last character of a string will have bit 7 set +IS_MENU: + BYTE 8 ; количество пунктов + BYTE 'Hardware',#FF ; заголовок меню + DC 'Sprinter ZX ' + DC 'ZX Spectrum' + DC 'Pentagon 128' + DC 'Scorpion 256' + DC 'Pentagon 512' + DC 'Restart ' + DC 'RETURN' + DC ' ' ; маркер конца +.Size EQU $-IS_MENU + +;****************************** +;SP_DOS: +; LD B,#E2 +; LD A,#E1 +; JR TR_DOS1 +;TR_DOS: +; LD B,#EA +; LD A,#E1 +;TR_DOS1: +; CALL DOS_ON +; CALL SET_ROM_PAGES +; CALL DOS_OFF +; RET +;****************************** + + +;************************************************** + +AY8910_X: + CALL AY8910 + JR SPRINTER_2XX + +SPRINTER_1X: + CALL SPRINTER_1 +; JR SPRINTER_2XX +SPRINTER_2XX: + CALL SPRINTER_ + CALL DOS_OFF + CALL CLS + RET + +SPRINTER_2X: + CALL SPRINTER_2 + CALL DOS_OFF + XOR A + JP RESET_1 ; сброс без перезагрузки + +AY8910: + LD D,#35 + CALL CMOS_RD + OR 1 + CALL CMOS_WR + + LD BC,204Eh ; port all_mode + IN A,(C) + AND #FE + OUT (C),A ; ACC_OFF + + LD A,PG_AY + JR SPRINTER_ALL + +SPRINTER_1: + LD D,35H + CALL CMOS_RD + AND 0FEh + CALL CMOS_WR + + LD BC,204Eh + IN A,(C) + AND 0FEh + OUT (C),A ; ACC_OFF + + LD A,PG_SP1 + JR SPRINTER_ALL +; +SPRINTER_2: + LD BC,204Eh + IN A,(C) + OR 01h ; ACC_ON + OUT (C),A + LD A,PG_SP2 + +SPRINTER_ALL: + +; RET + + EX AF,AF' + DI +; CALL DOS_ON + LD A,CNF_0 + OUT (CNF_PORT),A + + XOR A + LD BC,1FFDH + OUT (C),A + LD A,10H + LD B,7FH + OUT (C),A + + LD C,PAGE3 + IN B,(C) + + LD A,SYS_PAGE + OUT (C),A + + LD DE,CNF_0+D_TBON + LD HL,L_PENTAGON + LD (CONFIG_ALL),HL + LD (CONFIG_DE),DE + + LD A,B + LD (COPY_PAGE3),A + + EX AF,AF' + +; LD HL,CONFIG_BYTE +; +; CP (HL) +; JR NZ,SET_CONFIG1 +; INC HL +; LD A,(HL) +; AND A +; JP Z,NO_SET_CONFIG +; +; DEC HL +; LD A,(HL) +;SET_CONFIG1: +; +; LD (HL),A +; INC HL +; LD (HL),0 + + LD (SYS_SP),SP + + LD SP,SYS_SP-2 + + PUSH IY + PUSH IX + + PUSH HL + PUSH DE + PUSH BC + PUSH AF + + EXX + EX AF,AF' + + PUSH HL + PUSH DE + PUSH BC + PUSH AF + + EXX +; EX AF,AF' +; EX AF,AF' + + LD A,R + PUSH AF + LD A,I + PUSH AF + + LD (SP_SAVE),SP + + IN A,(PAGE2) + LD (COPY_PAGE2),A + IN A,(PAGE1) + LD (COPY_PAGE1),A + IN A,(PAGE0) + LD (COPY_PAGE0),A + +; LD A,1 +; OUT (PAGE2),A + +; LD HL,0BFE0H +; LD DE,0C0A0H +; LD BC,20H +; LDIR + +; LD HL,PROG_RETS +; LD DE,0BFE0H +; LD BC,20H +; LDIR + +; LD A,40H +; OUT (PAGE2),A + +; LD HL,0BFE0H +; LD DE,0C080H +; LD BC,20H +; LDIR + +; LD HL,PROG_RETS +; LD DE,0BFE0H +; LD BC,20H +; LDIR + +; JP RESTART_RETS_X + + LD HL,(CONFIG_BYTE) + LD IX,(CONFIG_BYTE) + + LD A,0FFH + OUT (PAGE2),A + + LD A,10H + LD BC,1FFDH + OUT (C),A ; 8-я страница !! + + IN A,(PAGE3) + EX AF,AF' + + CP PG_SP1 + LD DE,0FFFEh + JR Z,INT_PLD + CP PG_SP2 + LD DE,0FFFDh + JR Z,INT_PLD + CP PG_AY + LD DE,0FFFCh + JR Z,INT_PLD + + OUT (PAGE3),A ; ROM с прошивкой + LD DE,(0C090H) +INT_PLD: + EX AF,AF' + + OUT (PAGE3),A + + LD XH,D + LD XL,E + + AND A + SBC HL,DE + JP Z,RESTART_RETS_X + JP RESTART_RETS_X + +;================================ + +; CALL DOS_ON + +; XOR A +; OUT (0FH),A + +; CALL DOS_OFF + +; EX AF,AF' +; OUT (PAGE3),A + +; LD HL,0C000H +; LD DE,08000H +; LD BC,4000H +; LDIR ; not Configs for Sp-2000!!! + +; LD A,0FFH ; CASH !! +; OUT (PAGE3),A + +; LD HL,08000H +; LD DE,0C000H +; LD BC,4000H +; LDIR ; not Config for Sp-2000!!! + +RESET_ALL: + LD A,10H + LD BC,1FFDH + OUT (C),A ; 8-я страница !! + + LD A,0A0H + OUT (PAGE3),A + LD A,D_TBOFF + OUT (CNF_PORT),A +RESET_L: + LD (0C000H),A ; RESET !!! + JR RESET_L + DI + HALT + +;================================= + +PROG_RETS: + LD HL,RESTARTS ; 3 + LD B,16 ; 2 +REST_LOOPS: + LD (HL),0 ; 2 + INC HL ; 1 + DJNZ REST_LOOPS ; 2 + NOP ; 1 + NOP ; 1 + LD A,1 ; 2 + OUT (PAGE3),A ; 2 + + JP RESTART_RETS + +RESTART_RETS_X: + + +RESTART_RETS: + XOR A + OUT (0EFH),A ; зануление WAIT-ов + + LD BC,1FFDH + OUT (C),A + LD A,10H + LD B,7FH + OUT (C),A + + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD (CONFIG_BYTE),IX + EX AF,AF' + OUT (PAGE3),A + EX AF,AF' + + LD A,XL + CP #FF + LD A,128 + JR Z,YES_CBL + XOR A +YES_CBL: + LD BC,78 + OUT (C),A + + LD A,XL + OR #FE + LD BC,204Eh ;ALL_MODE + OUT (C),A + + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + +; LD A,1 +; OUT (PAGE2),A +; LD DE,0BFE0H +; LD HL,0C0A0H +; LD BC,20H +; LDIR + +; LD A,40H +; OUT (PAGE2),A + +; LD DE,0BFE0H +; LD HL,0C080H +; LD BC,20H +; LDIR + + LD A,(COPY_PAGE0) + OUT (PAGE0),A + LD A,(COPY_PAGE1) + OUT (PAGE1),A + LD A,(COPY_PAGE2) + OUT (PAGE2),A + + LD SP,(SP_SAVE) + + POP AF + LD I,A + POP AF + LD R,A + EXX + EX AF,AF' + + POP AF + POP BC + POP DE + POP HL + + EXX + EX AF,AF' + + POP AF + POP BC + POP DE + POP HL + + POP IX + POP IY + + LD SP,(SYS_SP) + IM 1 + + LD A,(BORDER) + RRCA + RRCA + RRCA + AND 7 + OUT (0FEH),A + + CALL DOS_ON + + LD A,3CH + OUT (0FFH),A + + CALL DOS_OFF + +NO_SET_CONFIG: + + LD A,(COPY_PAGE3) + OUT (PAGE3),A + AND A +; CALL DOS_ON + EI + RET + +;***************************** +;PROG_SINC: +; DI +; LD C,PAGE1 +; IN A,(C) +; LD B,0FFH +; OUT (C),B +; LD (7FFEH),BC +; LD (7FFCH),SP +; LD SP,7FF0H +; CALL PROG_SCR +; LD SP,(7FFCH) +; LD BC,(7FFEH) +; OUT (C),B +; EI +; RET + + +CNF_SC_312: + DI + CALL DOS_ON + LD A,CNF_0 + OUT (CNF_PORT),A + LD A,61H ; set 312 lines + OUT (0BDh),A + CALL DOS_OFF + LD HL,L_SCORPION + RET + +CNF_PN_320: + DI + CALL DOS_ON + LD A,CNF_0 + OUT (CNF_PORT),A + LD A,41H ; set 320 lines + OUT (0BDh),A + CALL DOS_OFF + LD HL,L_PENTAGON + RET + +SPRINTER_: + CALL CNF_PN_320 + LD DE,CNF_0+D_TBON + JR CONFIG_SET1 + +SCORPION: + CALL CNF_SC_312 + LD DE,CNF_1+D_TBON + JR CONFIG_SET1 + +PENTAGON: + CALL CNF_PN_320 + LD DE,CNF_2+D_TBON + JR CONFIG_SET1 + +PENTAGON512: + CALL CNF_PN_320 + LD DE,CNF_2+D_TBON+CNF_512 +; JR CONFIG_SET1 + +CONFIG_SET1: + CALL DOS_ON + CALL CONFIG_SET + CALL DOS_OFF + EI + RET + +CONFIG_SET: + EI + HALT + DI + + IN A,(PAGE3) + EX AF,AF' + LD A,SYS_PAGE + OUT (PAGE3),A + LD (CONFIG_ALL),HL + LD (CONFIG_DE),DE + EX AF,AF' + OUT (PAGE3),A + + PUSH DE + + CALL SINC_DEF + + LD HL,4104H + LD E,0 + LD B,4 + CALL LP_OPEN_S + + LD HL,5104H + LD E,0 + LD B,4 + CALL LP_OPEN_S + + POP DE + LD A,E + OUT (CNF_PORT),A + + RET + +; LD A,CNF_0 +; OUT (CNF_PORT),A +; CALL DOS_OFF +; JP 0 + +;***************************** +; *** MENU UTILITES *** +;***************************** + +UTILIT: + LD HL,C_DISK_C + CALL CALL_DOS1 + + CALL DOS_ON + LD A,0 ; DETECT_HDD + CALL HDD_EXE + CALL DOS_OFF + + JR C,DISK_UTILIT + + LD HL,C_DISK_C3 + CALL CALL_DOS1 + +UTIL_DISK: + LD A,0 + CALL EMM_FN3 + + LD HL,C_DEMO6 + CALL CALL_DOS1 + + LD A,0 + CALL GET_RAMD_ST + JR C,UTIL_DISK_L1 + JR Z,UTIL_DISK_L1 + + LD HL,C_DISK_C1 + CALL CALL_DOS1 + LD HL,C_DISK_C2 + CALL CALL_DOS1 + RET + +UTIL_DISK_L1: + LD A,(OPER_DISK) + INC A + CP 2 + JR Z,DISK_UTIL_RET + LD HL,C_DISK_C5 + CALL CALL_DOS1 + JR UTIL_DISK + +DISK_UTIL_RET: + LD HL,C_DISK_C6 + CALL CALL_DOS1 + RET + +DISK_UTILIT: + LD HL,C_DISK_C4 + CALL CALL_DOS1 + JR UTIL_DISK + +C_DEMO6: DB C_DEMO6_L,ZX_Token.rem,':/disk.trd',13,80 +C_DEMO6_L EQU $-C_DEMO6-1 + +;*************************************** + +;VERSION: DEFB 22,21,0 +; DEFB 16,2,"Expansion 3.02  1997 ELSY Co.",16,1,0FFh + +;********************************************** + +DOS_RUN: + LD HL,C_DISK_C1 + CALL CALL_DOS1 + + LD HL,C_DISK_C2 + CALL CALL_DOS1 + RET + +START_TRD: + LD HL,C_DISK_C + CALL CALL_DOS1 + LD HL,C_DISK_C0 + CALL CALL_DOS1 + LD HL,C_DISK_C3 + CALL CALL_DOS1 + RET + +C_DISK_C: DB C_DISK_CL,ZX_Token.rem,':',13,80 +C_DISK_CL EQU $-C_DISK_C-1 + +C_DISK_C0: DB C_DISK_C0L,ZX_Token.rem,':/CLEAR E',13,80 +C_DISK_C0L EQU $-C_DISK_C0-1 + +C_DISK_C1: DB C_DISK_C1L,ZX_Token.rem,':/RMD E',13,80 +C_DISK_C1L EQU $-C_DISK_C1-1 + +C_DISK_C2: DB C_DISK_C2L,ZX_Token.rem,':RUN',13,80 +C_DISK_C2L EQU $-C_DISK_C2-1 + +C_DISK_C3: DB C_DISK_C3L,ZX_Token.rem,':/HDD',13,80 +C_DISK_C3L EQU $-C_DISK_C3-1 + +C_DISK_C4: DB C_DISK_C4L,ZX_Token.rem,':/FDD',13,80 +C_DISK_C4L EQU $-C_DISK_C4-1 + +C_DISK_C5: DB C_DISK_C5L,ZX_Token.rem,':/B:',13,80 +C_DISK_C5L EQU $-C_DISK_C5-1 + +C_DISK_C6: DB C_DISK_C6L,ZX_Token.rem,':/A:',13,80 +C_DISK_C6L EQU $-C_DISK_C6-1 +; \ No newline at end of file diff --git a/ZX_EXP/TEST.ASM b/ZX_EXP/TEST.ASM new file mode 100644 index 0000000..b08dd4d --- /dev/null +++ b/ZX_EXP/TEST.ASM @@ -0,0 +1,340 @@ +; "MENU PROGRAMS." +;********************************* +; FOR INCLUDE !!! +;********************************* +; Обслуживание МЕНЮ +; HL - адрес таблицы программ +; A - номер в таблице +; если A=-1, то возврат, +; иначе выполнение +;***************************** +EXEC_PNT: + INC A + RET Z +EXEC_PNT_LOOP: + DEC A + JR Z,EXEC_HL + INC HL + INC HL + JR EXEC_PNT_LOOP +EXEC_HL: + LD E,(HL) + INC HL + LD D,(HL) + INC HL + EX DE,HL + JP (HL) +RET_FROM_M: + POP HL + RET + +;**************************************** +; РАБОТА С MENU_128 +;**************************************** +; ВХОД: - DE - адрес меню +; BC - длина меню +; ВЫХОД: A - выбранный пункт +;**************************************** + +RUN_MENU: + PUSH IX + PUSH DE ; DE - MENU + PUSH BC ; BC - len_menu + + LD A,(DE) + DEC A ; число CMD + CP 9 + JR C,RUN_M_1 +RUN_MN_ERR: + POP BC + POP DE + LD A,0FFh ; вернуться с номером 255 - ошибка + POP IX + RET +RUN_M_1: + LD A,(5B00H) + CP #F5 ; СРАВНИТЬ А С КОДОМ PUSH AF + JR NZ,RUN_MN_ERR + + + LD HL,LEN_P_M + ADD HL,BC ; HL - нужная длина памяти + LD B,H ; HL -> BC + LD C,L + LD HL,(5C61h) ; HL - начало раб обл + + PUSH BC + PUSH HL + + CALL C_1655 ; получить рабочую память + + POP DE + PUSH DE ; начало раб обл + + LD BC,LEN_P_M ; длина программы меню + LD HL,MENU_128 ; сама программа + LDIR ; программа перемещена. DE - место для MENU + POP BC + PUSH BC ; начало программы + + PUSH DE ; сохранить место для MENU + LD DE,MN_128_S ; данные для shifter + CALL SHIFTER ; изменить данные в соответствии с положением в MEM + POP DE ; вернуть место для MENU + + POP HL ; начало программы + POP IX ; длина данных + POP BC ; длина меню + EX (SP),HL ; HL - меню + LDIR ; переместить данные MENU + + POP HL ; адрес программы MENU + PUSH HL + PUSH IX ; длина раб. обл + CALL JP_HL ; вызов программы MENU + POP BC ; вернуть длину раб обл + POP HL ; и начала программы + + PUSH AF ; сохранить номер выбора + CALL C_19E8 ; освободить память !! + POP AF ; вернуть номер выбора + POP IX + RET ; возврат из программы RUN_MENU + +;************************************** +; 128k MENU +;************************************** +MENU_PROG EQU 25B9h +SWAP_ROM EQU 5B00h +NEW_SP EQU 1F45h +RET_SP EQU 1F20h + +;************************************** +; Программа вызова MENU_128 из ОЗУ. +;************************************** +MN_128_S: + DB SH_1-MENU_128+1 + DB SH_2-MENU_128+1 + DB SH_3-MENU_128+1 + DB SH_4-MENU_128+1 + DB SH_5-MENU_128 + DB SH_6-MENU_128 + DB SH_7-MENU_128 + DB SH_8-MENU_128 + DB SH_9-MENU_128 + DB SH_A-MENU_128 + DB SH_B-MENU_128 + DB SH_C-MENU_128 + DB 0 +;************************************** +; Эта программа перемещается в озу +;************************************** +MENU_128: + LD A,0 + OUT (SYS_PORT_OFF),A + CALL SWAP_ROM + CALL NEW_SP + +SH_1: LD DE,MENU_DAT-MENU_128 + LD HL,0F6EAh + LD BC,4 + LDIR + +SH_2: LD HL,MENU_128_E-MENU_128 + LD (0F6EAh),HL +SH_3: LD HL,MENU_128_E2-MENU_128 + LD (0F6ECh),HL + JP MENU_PROG + +RET_M EQU $-MENU_128 +SH_4: LD HL,MENU_DAT-MENU_128 + LD DE,0F6EAh + LD BC,4 + LDIR + + CALL RET_SP + CALL SWAP_ROM + EX AF,AF' + LD A,0 + OUT (CNF_PORT),A + EX AF,AF' + RET +MENU_DAT: + DW 0,0 +MENU_128_E: + DB 8 + DB 0 +SH_5: DW RET_M + DB 1 +SH_6: DW RET_M + DB 2 +SH_7: DW RET_M + DB 3 +SH_8: DW RET_M + DB 4 +SH_9: DW RET_M + DB 5 +SH_A: DW RET_M + DB 6 +SH_B: DW RET_M + DB 7 +SH_C: DW RET_M +MENU_128_E2: + +LEN_P_M EQU $-MENU_128 + +;************************************** +; RST 30 - получение BC SPACES с адреса HL +; 19E8h - освобождение BC spaces с адреса HL + + ; HL - раб. ячейка + ; BC - адрес программы + ; DE - адрес данных для перемещения + +SHIFTER: + LD A,(DE) ; (DE) - SHIFT IN PROG + AND A + RET Z + INC DE + LD L,A + LD H,0 + + ADD HL,BC ; HL - ADRESS FOR SHIFT + LD A,(HL) ; (HL)=(HL)+BC + ADD A,C + LD (HL),A + INC HL + LD A,(HL) + ADC A,B + LD (HL),A + JR SHIFTER + +;******************************************* +; МЕНЮ : байт 1 - для результата +; байт 2 - число пунктов +1 +; название меню, код 0FFh ( COPY ) +; пункты меню окончание байтом с +; уст, 7-м битом + пустая строка !! +;******************************************* +BASIC_MENU: +; LD A,(HL) +; CP 'S' +; JP Z,SET_SYSTEM + + LD DE,(CH_ADR) + PUSH DE + LD (CH_ADR),HL + CALL_48X 1C8Ch ; SYMBOL VAR + CALL_48X 2BF1h ; input txt !!! + ; DE - adr + ; BC - len + PUSH DE + PUSH BC + INC DE + CALL TST_M + JR C,NO_EXE_M + CALL RUN_MENU + LD (IY+0),255 +NO_EXE_M: + POP BC + POP DE + LD (DE),A + POP HL + LD (CH_ADR),HL + JP SW_ROM +TST_M: + PUSH DE + PUSH BC + EX DE,HL + LD D,(HL) + INC D + LD A,255 + CPIR + JR NZ,ERR_TST_M +LOOP_TST_M: + LD A,B + OR C + JR Z,ERR_TST_M + + BIT 7,(HL) + INC HL + DEC BC + JR Z,LOOP_TST_M + DEC D + JR NZ,LOOP_TST_M + POP BC + POP DE + AND A + RET + +ERR_TST_M: + POP BC + POP DE + LD A,255 + SCF + RET + +;************************************** + +CALL_DOS_MOVE: + LD DE,5C00H-DOS_PROG_LEN + LD HL,DOS_PROG + LD BC,DOS_PROG_LEN + LDIR + RET +; +; HL - адрес команды, BC - длина команды +CALL_DOS1: + LD C,(HL) + INC HL + LD B,0 + LD DE,(5C59H) + LD (5C5DH),DE + LDIR + LD (5C61H),DE + LD (5C63H),DE + LD (5C65H),DE + CALL CALL_DOS_MOVE + JP 5C00H-DOS_PROG_LEN +CALL_DOS: + PUSH HL + PUSH DE + PUSH BC + PUSH AF + CALL CALL_DOS_MOVE + LD A,13H + LD (5C00H-DOS_PROG_LEN+DOS_PROG_A),A + POP AF + POP BC + POP DE + POP HL + JP 5C00H-DOS_PROG_LEN + +DOS_PROG: + PUSH AF + XOR A + OUT (SYS_PORT_OFF),A + POP AF + CALL 3D03H +DOS_PROG_A EQU $-DOS_PROG-1 + PUSH AF + LD A,0 + OUT (CNF_PORT),A + POP AF + RET +DOS_PROG_LEN EQU $-DOS_PROG + +CONT_PROG: + LD HL,1AFDH + PUSH HL + LD HL,5B00H + PUSH HL + LD DE,5C71H + LD BC,1 + LD A,0E8H + LD (DE),A + JP SW_ROM + + + + diff --git a/ZX_EXP2.ASM b/ZX_EXP2.ASM new file mode 100644 index 0000000..394d614 --- /dev/null +++ b/ZX_EXP2.ASM @@ -0,0 +1,87 @@ +; +; EXPANSION VER 3.00 (C) 1997 ELSY Co. + DEVICE ZXSPECTRUM128 + ORG 0 + + INCLUDE 'INC/SPRINT00.INC' ; константы, макросы.. +;****************************** +; ***** BEGIN EXPANSION ***** +;****************************** +START: + DI + HALT +;*************************** + BLOCK #10-$,255 + RET +;*************************** + BLOCK #38-$,255 + EI + RETI +;*************************** + BLOCK #66-$,#C9 + RETN + +;*************************** + BLOCK #100-$,255 + INCLUDE 'EXP/EXP_HDD.ASM' +; INCLUDE 'EXP_KBD.ASZ' + +;GLOBAL_RESET: +;GLOBAL_RESET_ALL: +; LD A,0 +; OUT (0FDH),A +; LD A,40H +; OUT (0FDH),A +; LD A,0 +; OUT (CNF_PORT),A +; LD HL,0 +; PUSH HL +; DI +; RET + + BLOCK #3FD0-$,255 + + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_ON),A + POP AF +HD_CMD_XR_RET: + RET + +HD_CMD_XR: + JP HD_CMD + +;************************ + BLOCK #3FE0-$,255 +SND_TEST_RET: + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + JP SND_TEST_RET + +;*************************** + BLOCK #3FE8-$,255 +HD_EXP_RET: + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_ON),A + JP HD_EXP_EXE + + BLOCK #3FF0-$,255 + +HD_DOS_RET: + PUSH AF + DI + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + JR HD_CMD_XR + +EXP_FNS_RET: + PUSH AF + LD A,D_ROM16OFF + OUT (SYS_PORT_OFF),A + JP HD_CMD_XR_RET + +;****************************************** + BLOCK #4000-$,255 +; +; diff --git a/inc/SPRINT00.INC b/inc/SPRINT00.INC new file mode 100644 index 0000000..798d88b --- /dev/null +++ b/inc/SPRINT00.INC @@ -0,0 +1,618 @@ +; ; .LION EQU 0 +; ; .FLEX EQU 1 +; ; .IBMK EQU 1 +; ; .PROJ1 EQU 0 +; ; .PROJ2 EQU 0 +; ; .PROJ3 EQU 1 +; ; .PROJ4 EQU 1 +; ; .PAL EQU 1 + +; FastRAM_ON EQU #FB ; Включение кэша IN A,(FastRAM_ON) - прибито гвоздями в конфе +; FastRAM_OFF EQU #7B ; Отключение кэша IN A,(FastRAM_OFF) - прибито гвоздями в конфе +; FastRam_BANK0 EQU #5C ; Переключение страниц кэша при FastRAM_ON в нулевом окне (bit0..1) +; ROM_BANK0 EQU #5C ; Переключение страниц ПЗУ при FastRAM_OFF в нулевом окне (bit0..3) +; ; ; bit4 - Write-Enable (0 - no Write; 1 - Write) + +; ;ELCOM256 EQU 1 ; режим ELCOM-256 + +; ;IF ELCOM256 = 1 +; Spec_Page EQU #41 ; страница для перехвата ресета и ещё чего-то +DCP_PAGE EQU #40 ; страница портов +; Port_All_Mode EQU #204E +; PORT_JOYSTICK EQU 0FFh + +D_TBON EQU 3 ; данные для включения TURBO +D_TBOFF EQU 2 ; данные для включения TURBO +D_ROM16ON EQU 1 +D_ROM16OFF EQU 0 +CNF_0 EQU 04h +CNF_1 EQU 0Ch +CNF_2 EQU 14h +; CNF_3 EQU 1Ch +CNF_512 EQU 80h ; включение Pentagon 128 + +CBL_DIR EQU 78 ; порт управления COVOX-Blaster +; ; bit 7 - 1 включить CBL +; COVOX_PORT EQU 79 + +; ;ELSE +; ;D_TBON EQU 03h ; данные для включения TURBO +; ;D_TBOFF EQU 02h ; данные для включения TURBO +; ;D_ROM16ON EQU 40h +; ;D_ROM16OFF EQU 00h +; ;CNF_0 EQU 04h +; ;CNF_1 EQU 0Ch +; ;CNF_2 EQU 14h +; ;CNF_3 EQU 1Ch +; ;ENDIF + +; SPRINTER EQU 4 ; ВЕРСИЯ !!! +; ISD_WARM EQU 3 +; ISD_COLD EQU 0 +; IS_KEY EQU 40h ; ДАННЫЕ ДЛЯ ВЫВОДА В ПОРТ ПРИ ВКЛЮЧЕНИИ IS-DOS +; IS_RAM_ADR EQU 5BC0h ; АДРЕС ЗАГРУЗКИ ПРОГРАММЫ ПЕРЕХОДА В IS-DOS +; TB_WAITES EQU 00H ; WAITы для TURBO-MODE +; NTB_WAITES EQU 00H ; WAITы для neTURBO-MODE +; IS_WAITES EQU 00H ; WAITы для IS-DOS +; ;**************************************** +; ;SYS_PORT_ON EQU 07DH +; ;SYS_PORT_OFF EQU 03DH +;SYS_PORT_ON EQU CNF_PORT ;07CH +SYS_PORT_ON EQU #7C +SYS_PORT_OFF EQU #3C +; Port_VSYNC EQU #BD ; #61 - 320Lines/49Hz, #41 - 312Lines/50Hz +; .SET_320L EQU #41 +; .SET_312L EQU #61 + + +; PAL_V_PAGE EQU 09EH +; PAL_SCP EQU 01EH + +; ;CNF_PAGE EQU 040H +SYS_PAGE EQU 0FEH +; MODE_PAGE EQU 0FCH +; ;SCR_PAGE EQU 0FDH +; ;PAL_PAGE EQU 0FDH + +; KBD_COM EQU 1Bh +; KBD_DAT EQU 1Ah + +COM_B EQU 1Bh +; DAT_B EQU 1Ah +COM_A EQU 19h +; DAT_A EQU 18h + +LPT1_D EQU 1CH +LPT1_C EQU 1DH +LPT2_D EQU 1EH +LPT2_C EQU 1FH + +STC0_C EQU 10H +; STC1_C EQU 11H +; STC2_C EQU 12H +; STC3_C EQU 13H + +; RAMD_LET EQU ('R'-'A') + +; P_KBD_OUT EQU 0F8H +; P_KBD_IN EQU 0FEH + +; RESET_PAGE EQU #A0 +; ;----------[CMOS Registers]------------- +; CMOSREGS.ScreenSET EQU #1A +; ; %0000 1111 - COLOR STYLE For Setup +; ; %0011 0000 - Frame Int (Default/Pentagon/Scorpion/Original) +; ; %1100 0000 - Screen V-Sinc (Default/312 lines 50 HZ/320 Lines 49 Hz) +; SCREEN_HZ EQU #C0 +; SCREEN_INT EQU #30 +; ;--------------------------------------- +; ;*************************************** +; ;HD_HEADS EQU 5 +; HD_CS EQU 0A0H + +; ;HD_S_P_T EQU 17 +; ;HD_S_X_H EQU (HD_S_P_T * HD_HEADS) ; ??? число секторое на цилиндре + +P_DATS EQU 050H ; READ/WRITE INIR/OTIR + +P_ERR EQU 051H ; READ +; P_PREC EQU 151H ; WRITE + +P_S_CNT EQU 152H ; +P_S_NUM EQU 153H +P_C_LOW EQU 154H +P_C_HIG EQU 155H ;<-\ +P_HD_CS EQU 4152H ;<-/ + +P_HDST EQU 4053H ; READ +P_CMD EQU 4153H ; WRITE + +; P_HD3F6 EQU 4154H ; WRITE 3F6 +; P_HD3F7 EQU 4055H ; READ 3F7 + +; ;*************************************** +CMOS_DRD equ 0FFBDh +CMOS_DWR equ 0BFBDh +CMOS_AWR equ 0DFBDh +ISA_PORT equ 09FBDh + +; ;*************************************** +SEC_SIZE EQU 11 +CLAST_SIZE EQU 13 +RESERV_SECS EQU 14 +FATS_NUM EQU 16 +FLS_NUM EQU 17 +; S_P_D EQU 19 +FORM_CODE EQU 21 +S_P_F EQU 22 +S_P_T EQU 24 +H_P_S EQU 26 +SPECIAL_SECS EQU 28 +FAT_ID EQU 36H +; ;*************************************** + +; SYSTEM_ID EQU 0C020H + +SYS_SP EQU 0C0FEH ; ДОПОЛНИТЕЛЬНЫЙ СТЕК +DISK_TYPE EQU 0C100H ; переадресация дисков +COPY_PAGE0 EQU 0C104H ; копии значений портов страниц +COPY_PAGE1 EQU 0C105H +COPY_PAGE2 EQU 0C106H +COPY_PAGE3 EQU 0C107H +; RAMD_VARS EQU 0C108H ; переменные RAM-дисков +; A_RAMD_VARS EQU 0C118H ; текущий RAM-Disk +SP_SAVE EQU 0C11AH ; место для сохранения адреса стека +; ERR_SAVE EQU 0C11CH +COPY_RGADR EQU 0C11DH +; RAM_MSD EQU 0C11EH ; страница для работы в MS-DOS +MSD_SECS EQU 0C11FH ; Число секторов в MS-DOS +; MSD_NAME EQU 0C120H ; Адрес имени найденного файла +; MSD_FAT_SEC EQU 0C122H ; начальный сектор FAT +; INT_ADRESS EQU #C124 ; адрес обработчика прерывания +; INT_PAGE EQU #C126 ; страница с обработчиком прерывания +; INT_ID EQU #C127 ; если = #AA - значить обработчик установлен +DS_1440 EQU 0C128H ; флаги переключения 720/1440 +F_P_S EQU 0C129H ; число файловых записей в секторе +S_P_C EQU 0C12AH ; число секторов каталога +COUNT_FL EQU 0C12BH ; счетчик файлов в секторе +COUNT_SEC EQU 0C12CH ; счетчик секторов в каталоге +; C_P_B EQU 0C12DH ; число кластеров на блок ОЗУ +; ;CLASTER_LEN EQU 0C12EH ; длина кластера в байтах +FAT_FLAG EQU 0C130H ; FAT флаг + FAT sector +MSD_CONT_SEC EQU 0C132H ; текущий сектор для MS-DOS +MSD_CONT_SEC2 EQU 0C134H +S_X_H EQU 0C136H ; количество секторов на цилиндре +CONFIG_ALL EQU 0C138H ; описатель конфигурации 2 байта +CONFIG_DE EQU 0C13AH ; описатель конфигурации 2 байта +CONFIG_BYTE EQU 0C13EH ; байт конфигурации + +WIN_MAP_SC EQU 0C140H +; WIN_TAB_SC EQU 0C142H +; WIN_SAV_HL EQU 0C144H +; WIN_SAV_DE EQU 0C146H +; WIN_SAV_BC EQU 0C148H +WIN_ZG EQU 0C14AH +; WIN_PLACE_WIN EQU 0C14CH +; WIN_GR_MAP EQU 0C14EH + +SYS_WORK1 EQU 0C150H +; SYS_WORK2 EQU 0C152H +; SYS_WORK3 EQU 0C154H +; SYS_WORK4 EQU 0C156H + +; WIN_MAP_LAB1 EQU 0C158H +WIN_MODE_SH EQU 0C15CH +WIN_MODE_SC EQU 0C15EH + +MSD_FAT_SEC EQU 0C160H ; начальный сектор FAT +MSD_FAT_SEC2 EQU 0C162H ; начальный сектор FAT +MSD_CAT_SEC EQU 0C164H ; начальный сектор CAT +MSD_CAT_SEC2 EQU 0C166H ; начальный сектор CAT +MSD_DAT_SEC EQU 0C168H ; начальный сектор DAT +MSD_DAT_SEC2 EQU 0C16AH ; начальный сектор DAT +CLASTER_LEN EQU 0C16CH ; длина кластера в байтах +CLASTER_LEN2 EQU 0C16EH ; длина кластера в байтах + +; ;--------------------------------------------- +; ; vsync: +; ; bit0 1 - 320L, 0 - 312L +; ; bit1 1 - no waits, 0 - original waits +; VSyncAndWaits EQU #C170 +; ;--------------------------------------------- +; ;CMOS_FLAG_1 EQU 0C170H + +; GR_BIT_END EQU 7 + +; S_BIT_END EQU 7 +; S_BIT_LIN EQU 6 +; S_BIT_MOD EQU 5 + +; BIT_1440 EQU 1 +BIT_MASK_1440 EQU 00000010B + +RAMD_KEYS EQU 0C180H ; ключи RAM-Disks +RAMD_KEY_NUM EQU 16 + +; LIB_TABLE EQU 0C1A0H ; таблицы librares 32 байта +; ; +0 библиотека DOS +; ; +; HDD_INI_TABLE EQU 0C1C0H ; таблицы для ide устройств 32 байта +; ; 0 - копия DRV_HEAD +; ; 1 - секторов на дорожке +; ; 2 - число головок +; ; 3 - количество цилиндров мл. +; ; 4 - количество цилиндров старший. +; ; 5 - секторов на цилиндр мл. +; ; 6 - секторов на цилиндр старш. +; ; 7 - reserv - type +; ; +; FDD_INI_TABLE EQU 0C1E0H ; таблицы для FDD устройств 32 байта + +RAMD_FAT EQU 0C200H ; расположение блоков RAM-Disk-ов + +MS_BPB EQU 0C400H ; буфер BPB +MS_DIR EQU 0C800H ; буфер DIR sector +MS_FAT EQU 0CC00H ; буфер FAT sector +; MS_BUF EQU 0D000H ; буфер DAT sector +HD_IDF_ADR EQU #C600 +; .heads EQU HD_IDF_ADR+6 +; .sectors EQU HD_IDF_ADR+12 +; .sec_cyl EQU HD_IDF_ADR+4 + + +WIN_MAP_IX EQU 0E000H ; данные карт окон + +TASK_DATA EQU 0EC00H ; данные для задач + +; ; ***** - Оконные переменные - ***** + +WIN_SIZE_H EQU 0 ; горизонтальный размер в знакоместах +WIN_SIZE_V EQU 1 ; вертикальный размер в знакоместах +WIN_PLACE_H EQU 2 ; положение по горизонрали, в знакоместах +WIN_PLACE_V EQU 3 ; положение по вертикали в знакоместах +WIN_MODE EQU 4 ; режим знакоместа +WIN_MODE_S EQU 5 ; дополнительный режим +; ; бит 0 - Sp-SCR, +WIN_GR_X EQU 6 ; положение по X в поле графики (по знакомест) +WIN_GR_Y EQU 7 ; положение по Y в поле графики (по знакомест) + +WIN_HL EQU 8 ; сохранение HL +WIN_BC EQU 10 ; сохранение BC +WIN_DE EQU 12 ; сохранение DE +WIN_V_BEG EQU 14 ; начало окна по вертикали +WIN_V_END EQU 15 ; конец окна по вертикали +WIN_H_BEG EQU 16 ; начало окна по горизонтали +WIN_H_END EQU 17 ; конец окна по горизонтали +WIN_SIZE_REL EQU 18 ; реальный размер в символах +WIN_MODE_E EQU 19 ; дополнительный режим экрана +WIN_WORK_1 EQU 20 ; рабочая переменная 1 +WIN_WORK_2 EQU 21 ; рабочая переменная 2 +WIN_GRAF_X EQU 24 ; начальная координата по X +WIN_GRAF_Y EQU 26 ; начальная координата по Y + +; USER_VARS EQU 0F000h ; переменные пользователей + +; ;SW_ROM EQU 3CF9H + +; ; IF .PROJ4 +; ;RGADR EQU 0D0H +; ;RGSCR EQU 0D1H +; ;RGMOD EQU 0D2H +; ;RGACC EQU 0D3H +; ;PGACC EQU 0FCH + +; ;PAGE0 EQU 0C0H +; ;PAGE1 EQU 0C5H +; ;PAGE2 EQU 0C2H +; ;PAGE3 EQU 0C0H + +; ; ELSE + +; ; ENDIF + +PAGE0 EQU 082H +PAGE1 EQU 0A2H +PAGE2 EQU 0C2H +PAGE3 EQU 0E2H + +; ;RGADR EQU 089H +; ;RGSCR EQU 0A9H +; ;RGMOD EQU 099H +; ;RGACC EQU 0B9H +RGADR EQU #89 +; PORT_Y EQU #89 +; ;RGSCR EQU 0E9H +RGMOD EQU 0C9H +; ;RGACC EQU 0A9H +; ;PGACC EQU 0FCH + +CNF_PORT EQU 74h + +ALTERA EQU 1400H + +WG_COM EQU 00FH +WG_TRK EQU 03FH +WG_SEC EQU 05FH +WG_DATA EQU 07FH +P_DOS_FF EQU 0FFH + +; BUFER_RD EQU 5D25H + +PR_BUFER EQU 05B00H +AUTO_5B08 EQU 05B08H +AUTO_5B5C EQU 05B5CH +; COPY_P128 EQU 05B5CH +AUTO_5BFF EQU 05BFFH +K_STATE EQU 05C00H +KEY_TIME EQU 05C09H +REP_K_TYME EQU 05C10H +ZG EQU 05C36H +ERR_BEEP EQU 05C38H +; KEY_BEEP EQU 05C39H +ERR_NR EQU 05C3AH +FLAGS EQU 05C3BH +TV_FLAG EQU 05C3CH +ERR_SP EQU 05C3DH +; LIST_SP EQU 05C3FH +; MODE EQU 05C41H +NEW_PPC EQU 05C42H +NEW_S_PPC EQU 05C44H +PPC EQU 05C45H +; SUB_PPC EQU 05C47H +BORDER EQU 05C48H +; EDIT_PPC EQU 05C49H +BAS_VARS EQU 05C4BH +; WORK_VAR EQU 05C4DH +CHANS EQU 05C4FH +CUR_CHL EQU 05C51H +BAS_PROG EQU 05C53H +; NEXT_LINE EQU 05C55H +DATA_ADR EQU 05C57H +E_LINE EQU 05C59H +K_CUR EQU 05C5BH +CH_ADR EQU 05C5DH +; SINT_ER_AD EQU 05C5FH +WORK_SP EQU 05C61H +STK_BOT EQU 05C63H +STK_END EQU 05C65H +; B_REG EQU 05C67H +; MEM_CALC EQU 05C68H +; FLAGS_2 EQU 05C6AH +L_SCR_SIZE EQU 05C6BH +; AUTO_LST_L EQU 05C6CH +; OLD_PPC EQU 05C6EH +; OLD_S_PPC EQU 05C70H +; FLG_INPUT EQU 05C71H +; S_VAR_LEN EQU 05C72H +; SINT_TB_ADR EQU 05C74H +; RAND_SEED EQU 05C76H +; FRAMES EQU 05C78H +UDG EQU 05C7BH +; X_Y_COORD EQU 05C7DH +; PRN_POS EQU 05C7FH +; ADR_PR_BUF EQU 05C80H +; ECHO_E EQU 05C82H +; SCR_PL_M EQU 05C84H +SCR_PL_L EQU 05C86H +; SCR_POS_M EQU 05C88H +SCR_POS_L EQU 05C8AH +; SCROLL_ST EQU 05C8CH +ATTR_P EQU 05C8DH +; MASK_P EQU 05C8EH +ATTR_T EQU 05C8FH +; MASK_E EQU 05C90H +; FLAGS_ATR EQU 05C91H +MEM_BOT EQU 05C92H +AUTO_5C9A EQU 05C9AH +; NMI_ADR EQU 05CB0H +TOP_CLEAR EQU 05CB2H +P_RAMTOP EQU 05CB4H +BEG_ADRESS EQU 05CB6H +RET_INS EQU 05CC2H +AUTO_5CC3 EQU 05CC3H +DISK_A EQU 05CC8H +DISK_B EQU 05CC9H +DISK_C EQU 05CCAH +DISK_D EQU 05CCBH +CAT_SEC EQU 05CCCH +DRV_READY EQU 05CCDH +RD_WR_COM EQU 05CCEH +VAR_1 EQU 05CCFH +AUTO_5CD1 EQU 05CD1H +AUTO_5CD2 EQU 05CD2H +AUTO_5CD3 EQU 05CD3H +AUTO_5CD5 EQU 05CD5H +DOS_ERROR EQU 05CD6H +MED_START EQU 05CD7H +DOS_CH_ADR EQU 05CD9H +MED_LEN EQU 05CDBH +FL_NAME EQU 05CDDH +FL_N_2 EQU 05CDFH +FL_N_4 EQU 05CE1H +FL_N_6 EQU 05CE3H +FL_N_7 EQU 05CE4H +FL_TYPE EQU 05CE5H +FL_START EQU 05CE6H + +FL_LEN EQU 05CE8H +START_CLASTER EQU FL_LEN + +FL_SIZE EQU 05CEAH +FL_PLACE EQU 05CEBH +VAR_2 EQU 05CEDH +INTERF_I EQU 05CEFH +VAR_2_0 EQU 05CF1H +VAR_2_1 EQU 05CF2H +CONT_SEC EQU 05CF4H +CONT_TRK EQU 05CF5H +OPER_DISK EQU 05CF6H +DOS_FLAG EQU 05CF7H +DISK_1_FLG EQU 05CF8H +DISK_2_FLG EQU 05CF9H +TIME_A EQU 05CFAH +TIME_B EQU 05CFBH +TIME_C EQU 05CFCH +TIME_D EQU 05CFDH +COMAND_WG EQU 05CFEH +SEC_NUM EQU 05CFFH +CONT_BUF_ADR EQU 05D00H +WORK_2 EQU 05D02H +WORK_4 EQU 05D04H +S_NAME_NUM EQU 05D06H +N_DEL_FLS EQU 05D07H +FST_SYM_NAME EQU 05D08H +VAR_3 EQU 05D09H +BUF_FLAG EQU 05D0CH +BAS_DOS_FLG EQU 05D0EH +DOS_ERR_2 EQU 05D0FH +ERR_3D00 EQU 05D10H +ADR_DOS_COM EQU 05D11H +ERR_SP_COPY EQU 05D13H +MSG_FLAG EQU 05D15H +PDOS_COPY EQU 05D16H +FLAG_BOOT EQU 05D17H +INT_1_VAR EQU 05D18H +CONT_DISK EQU 05D19H +ADR_RET EQU 05D1AH +DOS_SP EQU 05D1CH +FL_NUMBER EQU 05D1EH +COM_LN_COPY EQU 05D20H +L_5D23 EQU 05D23H +BUFER EQU 05D25H +AUTO_5D33 EQU 05D33H +CLEAR_SEC EQU 05E06H +CLEAR_TRK EQU 05E07H +TYPE_DISK EQU 05E08H +N_FILES EQU 05E09H +FREE_SEC EQU 05E0AH +CODE_10H EQU 05E0CH +DISK_MRK_1 EQU 05E0FH +DISK_ALT_NM EQU 05E10H +N_DEL_FL EQU 05E19H +DISK_NAME EQU 05E1AH + + +; ;RET_PAGE0 MACRO +; ; LD A,0 +; ; OUT (PAGE0),A +; ; ENDM +; ;RET_PAGE1 MACRO +; ; LD A,5 +; ; OUT (PAGE1),A +; ; ENDM +; ;RET_PAGE2 MACRO +; ; LD A,2 +; ; OUT (PAGE2),A +; ; ENDM +; ;RET_PAGE3 MACRO +; ; LD A,0 +; ; OUT (PAGE3),A +; ; ENDM + +; MACRO DSS adr,dt +; BLOCK adr-$,dt +; ENDM + + MACRO CALL_48 adr + + PUSH HL + LD HL,.ADR_R + PUSH HL + LD HL,SW_ROM + PUSH HL + LD HL,adr + JP JP_HL_48 +.ADR_R: + POP HL + + ENDM + + MACRO CALL_48X adr + + PUSH HL + LD HL,.ADR_R + EX (SP),HL + + PUSH HL + LD HL,SW_ROM + EX (SP),HL + + PUSH HL + LD HL,adr + EX (SP),HL + + JP SW_ROM +.ADR_R: + ENDM + +; MACRO RAMD_KEY + +; SUB 4 +; DI +; LD B,SYS_PAGE +; LD C,PAGE3 +; OUT (C),B +; LD HL,RAMD_KEYS +; ADD A,L +; LD L,A +; DEC L +; LD (A_RAMD_VARS),HL +; INC L +; LD L,(HL) +; RET_PAGE3 +; EI +; LD A,L + +; ENDM + + + + +; ; Поpты Sprinter. (байты PORT_X)<< +; ; 0 - port FF<< +; ; 1 - port keyboard<< +; ; 2 - port BORDER<< +; ; 3 - port 1FFDh<< +; ; 4 - port 7FFDh<< +; ; 5 - port 3FFDh<< +; ; 6 - port Start-ROM<< +; ; 7 - port Start-ROM-ALT<< +; ; 8 - port ROM-BASIC48<< +; ; 9 - port ROM-BASIC128<< +; ; 10 - port ROM-TR-DOS<< +; ; 11 - port ROM-EXPANSION<< +; ; 12 - port ROM-BASIC48-ALT<< +; ; 13 - port ROM-BASIC128-ALT<< +; ; 14 - port ROM-TR-DOS-ALT<< +; ; 15 - port ROM-EXPANSION-ALT<< +; ; 16 ─┐<< +; ; .. ─┤<< +; ; 31 ─┴─ports RAM-PAGES - поpты указывающие, какая стpаница ОЗУ<< +; ;подключена в качестве стpаницы 0..F в конфигуpации Scorpion.<< +; ; 33 - port RAM-0 стpаница ОЗУ подключаемая в нулевое окно пpоцессоpа<< +; ; 34 - port RAM-5 стpаница ОЗУ подключаемая в пеpвое окно пpоцессоpа<< +; ; 35 - port RAM-2 стpаница ОЗУ подключаемая во втоpое окно пpоцессоpа<< +; ; 36 - port CONFIG<< +; ; 37 - port COVOX-1<< +; ; 38 - port COVOX-2<< +; ; 39 - port AY-3-8910-adr<< +; ; 40 - port AY-3-8910-dat<< +; ; 41 - port KEMPSTON<< +; ; 42 - port ISA-interface<< +; ; 43 ─┐reserv<< +; ; .. ─┤<< +; ; 47 ─┘<< +; ; 48 - 51 поpты ВГ93<< +; ; 52 - поpт DOS-1<< +; ; 53 - поpт DOS-2<< +; ; 54 ─┐ служебные поpты<< +; ; .. ─┤<< +; ; 63 ─┘<< +; ; 64 ─┐ поpты IDE interface<< +; ; .. ─┤<< +; ; 79 ─┘<< +; ; 80..127 - pезеpв.<< +; ; 128..143 - поpты идентификатоpа машины ( только для чтения.)<< +; ; 144..254 - pезеpв<< +; ; 255 - нуль-поpт - отключенное состояние.<< +; ; diff --git a/inc/char_codes.inc b/inc/char_codes.inc new file mode 100644 index 0000000..4d8c5c7 --- /dev/null +++ b/inc/char_codes.inc @@ -0,0 +1,203 @@ +; ZX Spectrum char codes +; By Marcos Cruz (programandala.net) + +; --------------------------------------------------------------------- +; Characters (new format) +ZX_Char: +; Not used equ 0x00 +; Not used equ 0x01 +; Not used equ 0x02 +; Not used equ 0x03 +; Not used equ 0x04 +; Not used equ 0x05 +.caps equ 0x06 ; toggle caps lock +.print_comma equ 0x06 ; (when printed) +.edit equ 0x07 ; edit +.esc equ 0x07 ; escape +.cursor_left equ 0x08 +.backspace equ 0x08 +.right equ 0x09 ; cursor right +.cursor_down equ 0x0A +.cursor_up equ 0x0B +.delete_left equ 0x0C ; delete (backspace) +.form_feed equ 0x0C ; used for printing +.carriage_return equ 0x0D ; enter key +.extended_mode equ 0x0E ; +.number equ 0x0E ; +.graphics equ 0x0F ; toggle graphics mode +.ink equ 0x10 +.paper equ 0x11 +.flash equ 0x12 +.bright equ 0x13 +.inverse equ 0x14 +.over equ 0x15 +.at equ 0x16 +.tab equ 0x17 ; tab (screen only) +; Not used equ 0x18 +; Not used equ 0x19 +; Not used equ 0x1A +; Not used equ 0x1B +; Not used equ 0x1C +; Not used equ 0x1D +; Not used equ 0x1E +; Not used equ 0x1F +.space equ 0x20 + +; Standart symbols +; #21 ! #22 " #23 # #24 $ #25 % #26 & #27 , #28 ( +; #29 ) #2A * #2B + #2C , #2D - #2F . #2F / #30 0 +; #31 1 #32 2 #33 3 #34 4 #35 5 #36 6 #37 7 #38 8 +; #39 9 #3A : #3B ; #3C < #3D = #3E > #3F ? #40 @ +; #41 A #42 B #43 C #44 D #45 E #46 F #47 G #48 H +; #49 I #4A J #4B K #4C L #4D M #4E N #4F O #50 P +; #51 Q #52 R #53 S #54 T #55 U #56 V #57 W #58 X +; #59 Y #5A Z #5B [ #5C / #5D ] #5E ^ #5F _ #60 ? +; #61 a #62 b #63 c #64 d #65 e #66 f #67 g #68 h +; #69 i #6A j #6B k #6C l #6D m #6E n #6F o #70 p +; #71 q #72 r #73 s #74 t #75 u #76 v #77 w #78 x +; #79 y #7A z #7B { #7C | #7D } #7E ~ +.copyright equ 0x7F ; copyright sign + +; --------------------------------------------------------------------- +; User Defined Graphics +; 0 1 2 3 4 5 6 7 +;| | | #| |# | |# #| | | | #| |# | |# #| +;| | | | | | | | | #| | #| | #| | #| +; 8 9 A B C D E F +;| | | #| |# | |# #| | | | #| |# | |# #| +;|# | |# | |# | |# | |# #| |# #| |# #| |# #| +ZX_UDG: +.block_0 equ 0x80 +.block_1 equ 0x81 +.block_2 equ 0x82 +.block_3 equ 0x83 +.block_4 equ 0x84 +.block_5 equ 0x85 +.block_6 equ 0x86 +.block_7 equ 0x87 +.block_8 equ 0x88 +.block_9 equ 0x89 +.block_A equ 0x8A +.block_B equ 0x8B +.block_C equ 0x8C +.block_D equ 0x8D +.block_E equ 0x8E +.block_F equ 0x8F +.a equ 0x90 +.b equ 0x91 +.c equ 0x92 +.d equ 0x93 +.e equ 0x94 +.f equ 0x95 +.g equ 0x96 +.h equ 0x97 +.i equ 0x98 +.j equ 0x99 +.k equ 0x9A +.l equ 0x9B +.m equ 0x9C +.n equ 0x9D +.o equ 0x9E +.p equ 0x9F +.q equ 0xA0 +.r equ 0xA1 +.s equ 0xA2 +.t equ 0xA3 ; token SPECTRUM in 128 basic +.u equ 0xA4 ; token PLAY in 128 basic +; --------------------------------------------------------------------- +; Tokens +ZX_Token +.spectrum equ 0xA3 ; UDG T in 48 basic +.play equ 0xA4 ; UDG U in 48 basic +.rnd equ 0xA5 +.inkey_dollar equ 0xA6 +.pi equ 0xA7 +.fn equ 0xA8 +.point equ 0xA9 +.screen_dollar equ 0xAA +.attr equ 0xAB +.at equ 0xAC +.tab equ 0xAD +.val_dollar equ 0xAE +.code equ 0xAF +.val equ 0xB0 +.len equ 0xB1 +.sin equ 0xB2 +.cos equ 0xB3 +.tan equ 0xB4 +.asn equ 0xB5 +.acs equ 0xB6 +.atn equ 0xB7 +.ln equ 0xB8 +.exp equ 0xB9 +.int equ 0xBA +.sqr equ 0xBB +.sgn equ 0xBC +.abs equ 0xBD +.peek equ 0xBE +.in equ 0xBF +.usr equ 0xC0 +.str_dollar equ 0xC1 +.chr_dollar equ 0xC2 +.not equ 0xC3 +.bin equ 0xC4 +.or equ 0xC5 +.and equ 0xC6 +.less_or_equal equ 0xC7 +.greater_or_equal equ 0xC8 +.not_equal equ 0xC9 +.line equ 0xCA +.then equ 0xCB +.to equ 0xCC +.step equ 0xCD +.def_fn equ 0xCE +.cat equ 0xCF +.format equ 0xD0 +.move equ 0xD1 +.erase equ 0xD2 +.open_channel equ 0xD3 +.close_channel equ 0xD4 +.merge equ 0xD5 +.verify equ 0xD6 +.beep equ 0xD7 +.circle equ 0xD8 +.ink equ 0xD9 +.paper equ 0xDA +.flash equ 0xDB +.bright equ 0xDC +.inverse equ 0xDD +.over equ 0xDE +.out equ 0xDF +.lprint equ 0xE0 +.llist equ 0xE1 +.stop equ 0xE2 +.read equ 0xE3 +.data equ 0xE4 +.restore equ 0xE5 +.new equ 0xE6 +.border equ 0xE7 +.continue equ 0xE8 +.dim equ 0xE9 +.rem equ 0xEA +.for equ 0xEB +.go_to equ 0xEC +.go_sub equ 0xED +.input equ 0xEE +.load equ 0xEF +.list equ 0xF0 +.let equ 0xF1 +.pause equ 0xF2 +.next equ 0xF3 +.poke equ 0xF4 +.print equ 0xF5 +.plot equ 0xF6 +.run equ 0xF7 +.save equ 0xF8 +.randomize equ 0xF9 +.if equ 0xFA +.cls equ 0xFB +.draw equ 0xFC +.clear equ 0xFD +.return equ 0xFE +.copy equ 0xFF +; --------------------------------------------------------------------- \ No newline at end of file diff --git a/inc/rom_routines.inc b/inc/rom_routines.inc new file mode 100644 index 0000000..4a011dd --- /dev/null +++ b/inc/rom_routines.inc @@ -0,0 +1,1160 @@ +; ZX Spectrum ROM routines + +; By Marcos Cruz (programandala.net) + +; ============================================================== +; History + +; 2014-12-17: Start. Regulary improved afterwards. +; +; 2015-05-24: Changes to make the file usable as symbols input +; file by z80dasm. +; +; 2015-08-20: Add new routines. +; +; 2015-09-06: Add new routines. +; +; 2016-04-16: Add new routines. +; +; 2016-08-19: Add all remaining routines, extracted from the ROM +; disassembly; remove duplicates; change all hex addresses to +; upper case. Rename `draw` to `draw_line_24BA; use `draw` for +; the official routine. + +; 2016-09-02: Rename the entry point of ADD-CHAR at 0F85 with a descriptive +; name. +; +; 2017-02-09: Add two additional entry points, and two 128K routines. + +; ============================================================== + +rom_0058: equ 0x0058 ; XXX TODO -- name +rom_abs: equ 0x346A +rom_acs: equ 0x3843 +rom_add_back: equ 0x3004 +rom_add_ch_1: equ 0x0F8B +rom_add_char: equ 0x0F81 +rom_add_char_keeping_current_mode: equ 0x0F85 +rom_add_rep_6: equ 0x309F +rom_addend_0: equ 0x2FF9 +rom_addition: equ 0x3014 +rom_addn_oflw: equ 0x303C +rom_all_added: equ 0x300D +rom_alpha: equ 0x2C8D +rom_alphanum: equ 0x2C88 +rom_arc_end: equ 0x245F +rom_arc_loop: equ 0x2425 +rom_arc_start: equ 0x2439 +rom_asn: equ 0x3833 +rom_atn: equ 0x37E2 +rom_auto_l_1: equ 0x17CE +rom_auto_l_2: equ 0x17E1 +rom_auto_l_3: equ 0x17E4 +rom_auto_l_4: equ 0x17ED +rom_auto_list: equ 0x1795 +rom_bc_spaces: equ 0x0030 +rom_be_end: equ 0x03F6 +rom_be_h_and_l_lp: equ 0x03D6 +rom_be_ix_plus_0: equ 0x03D4 +rom_be_ix_plus_1: equ 0x03D3 +rom_be_ix_plus_2: equ 0x03D2 +rom_be_ix_plus_3: equ 0x03D1 +rom_beep: equ 0x03F8 +rom_beeper: equ 0x03B5 +rom_bin_digit: equ 0x2CA2 +rom_bin_end: equ 0x2CB3 +rom_bits_zero: equ 0x3283 +rom_border: equ 0x2294 +rom_border_1: equ 0x22A6 +rom_border_2297: equ 0x2297 ; secondary entry to rom_border +rom_border_229b: equ 0x229B ; secondary entry to rom_border +rom_both_null: equ 0x3572 +rom_break_key: equ 0x1F54 ; return the carry flag reset if SHIFT and BREAK keys are both being pressed. +rom_byte_comp: equ 0x3564 +rom_byte_zero: equ 0x327E +rom_c_arc_ge1: equ 0x235A +rom_c_ent: equ 0x37B7 +rom_c_r_gre_1: equ 0x233B +rom_ca_10_by_a_plus_c: equ 0x2F8B +rom_calculate: equ 0x335B +rom_call_jump: equ 0x162C +rom_call_sub: equ 0x15F7 +rom_cases: equ 0x37FA +rom_cat_etc: equ 0x1793 +rom_cd_prms1: equ 0x247D +rom_ch_add_plus_1: equ 0x0074 +rom_chan_flag: equ 0x1615 +rom_chan_k: equ 0x1634 +rom_chan_op_1: equ 0x1610 +rom_chan_open: equ 0x1601 ; open a channel (channel in A) +rom_chan_p: equ 0x164D +rom_chan_s: equ 0x1642 ; set flags for upper screen channel +rom_chan_s_1: equ 0x1646 +rom_check_end: equ 0x1BEE +rom_chn_cd_lu: equ 0x162D +rom_chrs: equ 0x35C9 +rom_circle: equ 0x2320 +rom_cl_09_1: equ 0x1CD6 +rom_cl_addr: equ 0x0E9B +rom_cl_all: equ 0x0DAF ; clear whole (upper) display area +rom_cl_attr: equ 0x0E88 +rom_cl_chan: equ 0x0D94 +rom_cl_chan_a: equ 0x0DA0 +rom_cl_line: equ 0x0E44 +rom_cl_line_1: equ 0x0E4A +rom_cl_line_2: equ 0x0E4D +rom_cl_line_3: equ 0x0E80 +rom_cl_sc_all: equ 0x0DFE +rom_cl_scr_1: equ 0x0E05 +rom_cl_scr_2: equ 0x0E0D +rom_cl_scr_3: equ 0x0E19 +rom_cl_scroll: equ 0x0E00 ; scroll the number of lines in the B register +rom_cl_set: equ 0x0DD9 +rom_cl_set_1: equ 0x0DEE +rom_cl_set_2: equ 0x0DF4 +rom_cl_str_lu: equ 0x1716 +rom_class_01: equ 0x1C1F +rom_class_02: equ 0x1C4E +rom_class_03: equ 0x1C0D +rom_class_04: equ 0x1C6C +rom_class_05: equ 0x1C11 +rom_class_07: equ 0x1C96 +rom_class_09: equ 0x1CBE +rom_class_0b: equ 0x1CDB +rom_class_tbl: equ 0x1C01 +rom_clear: equ 0x1EAC +rom_clear_1: equ 0x1EB7 +rom_clear_2: equ 0x1EDC +rom_clear_prb: equ 0x0EDF +rom_clear_run: equ 0x1EAF +rom_clear_sp: equ 0x1097 +rom_close: equ 0x16E5 +rom_close_0_16EB: equ 0x16EB +rom_close_1: equ 0x16FC +rom_close_2: equ 0x1701 +rom_close_str: equ 0x171C +rom_cls: equ 0x0D6B ; CLS +rom_cls_1: equ 0x0D87 +rom_cls_2: equ 0x0D89 +rom_cls_3: equ 0x0D8E +rom_cls_lower: equ 0x0D6E +rom_co_change: equ 0x226C +rom_co_temp_1: equ 0x21E1 +rom_co_temp_2: equ 0x21E2 +rom_co_temp_3: equ 0x21F2 +rom_co_temp_4: equ 0x21FC +rom_co_temp_5: equ 0x2211 +rom_co_temp_6: equ 0x2228 +rom_co_temp_7: equ 0x2234 +rom_co_temp_8: equ 0x223E +rom_co_temp_9: equ 0x2246 +rom_co_temp_a: equ 0x2257 +rom_co_temp_b: equ 0x2258 +rom_co_temp_c: equ 0x2273 +rom_co_temp_d: equ 0x227D +rom_co_temp_e: equ 0x2287 +rom_code: equ 0x3669 +rom_comma_sp: equ 0x1537 +rom_continue: equ 0x1E5F +rom_copy: equ 0x0EAC +rom_copy_1: equ 0x0EB2 +rom_copy_2: equ 0x0EC9 +rom_copy_3: equ 0x0ED3 +rom_copy_buff: equ 0x0ECD +rom_copy_end: equ 0x0EDA +rom_copy_l_1: equ 0x0EFD +rom_copy_l_2: equ 0x0F0C +rom_copy_l_3: equ 0x0F14 +rom_copy_l_4: equ 0x0F18 +rom_copy_l_5: equ 0x0F1E +rom_copy_line: equ 0x0EF4 +rom_copyright: equ 0x1539 +rom_cos: equ 0x37AA +rom_count_one: equ 0x31FA +rom_cp_lines: equ 0x1980 +rom_ctlchrtab: equ 0x0A11 +rom_d_l_diag: equ 0x24D4 +rom_d_l_hr_vt: equ 0x24DB +rom_d_l_loop: equ 0x24CE +rom_d_l_plot: equ 0x24EC +rom_d_l_range: equ 0x24F7 +rom_d_l_step: equ 0x24DF +rom_d_letter: equ 0x2C1F +rom_d_no_loop: equ 0x2C2E +rom_d_rport_c: equ 0x2C05 +rom_d_run: equ 0x2C15 +rom_d_size: equ 0x2C2D +rom_data: equ 0x1E27 +rom_data_1: equ 0x1E2C +rom_data_2: equ 0x1E37 +rom_de_comma_paren_de_plus_1_paren: equ 0x2AEE +rom_dec_jr_nz: equ 0x367A +rom_dec_rpt_c: equ 0x2CCF +rom_dec_sto_1: equ 0x2CD5 +rom_dec_to_fp: equ 0x2C9B +rom_decimal: equ 0x2CCB +rom_def_fn: equ 0x1F60 +rom_def_fn_1: equ 0x1F6A +rom_def_fn_2: equ 0x1F7D +rom_def_fn_3: equ 0x1F86 +rom_def_fn_4: equ 0x1F89 +rom_def_fn_5: equ 0x1F94 +rom_def_fn_6: equ 0x1FA6 +rom_def_fn_7: equ 0x1FBD +rom_delete: equ 0x33A1 +rom_differ: equ 0x19DD +rom_dim: equ 0x2C02 +rom_dim_clear: equ 0x2C7C +rom_dim_sizes: equ 0x2C7F +rom_div_34th: equ 0x31DB +rom_div_loop: equ 0x31D2 +rom_div_start: equ 0x31E2 +rom_division: equ 0x31AF +rom_divn_expt: equ 0x313D +rom_dl_larger: equ 0x24CB +rom_dl_x_ge_y: equ 0x24C4 +rom_double_a: equ 0x338C +rom_dr_3_prms: equ 0x238D +rom_dr_prms: equ 0x23C1 +rom_dr_sin_nz: equ 0x23A3 +rom_draw: equ 0x2382 +rom_draw_line: equ 0x24B7 +rom_draw_line_24BA: equ 0x24BA ; entry point of the draw_line routine +rom_draw_save: equ 0x2497 +rom_drw_steps: equ 0x2420 +rom_e_divsn: equ 0x2D6D +rom_e_end: equ 0x2D7B +rom_e_fetch: equ 0x2D6E +rom_e_format: equ 0x2CEB +rom_e_fp_jump: equ 0x2D18 +rom_e_l_1: equ 0x1A15 +rom_e_line_no: equ 0x19FB +rom_e_loop: equ 0x2D60 +rom_e_save: equ 0x2D55 +rom_e_to_fp: equ 0x2D4F +rom_e_tst_end: equ 0x2D71 +rom_each_s_1: equ 0x1990 +rom_each_s_2: equ 0x1998 +rom_each_s_3: equ 0x199A +rom_each_s_4: equ 0x19A5 +rom_each_s_5: equ 0x19AD +rom_each_s_6: equ 0x19B1 +rom_each_stmt: equ 0x198B +rom_ed_again: equ 0x0F30 +rom_ed_blank: equ 0x1150 +rom_ed_c_done: equ 0x117C +rom_ed_c_end: equ 0x117E +rom_ed_contr: equ 0x0F6C +rom_ed_copy: equ 0x111D +rom_ed_cur: equ 0x1011 +rom_ed_delete: equ 0x1015 +rom_ed_down: equ 0x0FF3 +rom_ed_edge: equ 0x1031 +rom_ed_edge_1: equ 0x103E +rom_ed_edge_2: equ 0x1051 +rom_ed_edit: equ 0x0FA9 +rom_ed_end: equ 0x1026 +rom_ed_enter: equ 0x1024 +rom_ed_error: equ 0x107F +rom_ed_full: equ 0x1167 +rom_ed_graph: equ 0x107C +rom_ed_ignore: equ 0x101E +rom_ed_keys: equ 0x0F92 +rom_ed_keys_t: equ 0x0FA0 +rom_ed_left: equ 0x1007 +rom_ed_list: equ 0x106E +rom_ed_loop: equ 0x0F38 +rom_ed_right: equ 0x100C +rom_ed_spaces: equ 0x115E +rom_ed_stop: equ 0x1001 +rom_ed_symbol: equ 0x1076 +rom_ed_up: equ 0x1059 +rom_editor: equ 0x0F2C +rom_end_calc: equ 0x369B +rom_end_compl: equ 0x30A3 +rom_end_tests: equ 0x358C +rom_ent_table: equ 0x338E +rom_error_1: equ 0x0008 +rom_error_2: equ 0x0053 +rom_error_3: equ 0x0055 +rom_ex_or_not: equ 0x3543 +rom_exchange: equ 0x343C +rom_exit: equ 0x36C2 +rom_exp: equ 0x36C4 +rom_expt_1num: equ 0x1C82 +rom_expt_2num: equ 0x1C7A +rom_expt_exp: equ 0x1C8C +rom_f_found: equ 0x1D7C +rom_f_l_s: equ 0x1D34 +rom_f_loop: equ 0x1D64 +rom_f_reorder: equ 0x1D16 +rom_f_use_1: equ 0x1D10 +rom_fetch_num: equ 0x1CDE +rom_fetch_two: equ 0x2FBA +rom_find_i_1: equ 0x1E9C +rom_find_int1: equ 0x1E94 +rom_find_int2: equ 0x1E99 +rom_first_3d: equ 0x3380 +rom_fn_skpovr: equ 0x28AB +rom_for: equ 0x1D03 +rom_form_exp: equ 0x33DE +rom_fp_0_or_1: equ 0x350B +rom_fp_a_end: equ 0x2DE1 +rom_fp_calc: equ 0x0028 +rom_fp_calc_2: equ 0x33A2 +rom_fp_delete: equ 0x2DAD +rom_fp_to_a: equ 0x2DD5 +rom_fp_to_bc: equ 0x2DA2 +rom_free_mem: equ 0x1F1A +rom_frst_less: equ 0x3585 +rom_full_addn: equ 0x303E +rom_g_loop: equ 0x3453 +rom_gen_ent_1: equ 0x335E +rom_gen_ent_2: equ 0x3362 +rom_get_argt: equ 0x3783 +rom_get_char: equ 0x0018 +rom_get_hl_by_de: equ 0x2AF4 +rom_get_mem_xx: equ 0x340F +rom_get_param: equ 0x1B55 +rom_go_nc_mlt: equ 0x30A5 +rom_go_sub: equ 0x1EED +rom_go_to: equ 0x1E67 +rom_go_to_2: equ 0x1E73 +rom_go_to_bc: equ 0x1E6A ; 'GO TO' line in BC register +rom_go_to_hl: equ 0x1E6C ; 'GO TO' line in HL register XXX not tested +rom_gre.8: equ 0x373D +rom_greater_0: equ 0x34F9 +rom_hl_again: equ 0x30BC +rom_hl_by_de_to_hl: equ 0x30A9 ; HL=HL*DE +rom_hl_end: equ 0x30BE +rom_hl_hl_by_de: equ 0x30A9 +rom_hl_loop: equ 0x30B1 +rom_hlxde: equ 0x30A9 ; HL=HL*DE +rom_i_carry: equ 0x2AE8 +rom_i_restore: equ 0x2AEB +rom_if: equ 0x1CF0 +rom_if_1: equ 0x1D00 +rom_in: equ 0x34A5 +rom_in_assign: equ 0x21B9 +rom_in_chan_k: equ 0x21D6 +rom_in_item_1: equ 0x20C1 +rom_in_item_2: equ 0x20D8 +rom_in_item_3: equ 0x20ED +rom_in_next_1: equ 0x21AF +rom_in_next_2: equ 0x21B2 +rom_in_pk_stk: equ 0x34B0 +rom_in_pr_1: equ 0x211A +rom_in_pr_2: equ 0x211C +rom_in_pr_3: equ 0x2129 +rom_in_prompt: equ 0x20FA +rom_in_stop: equ 0x21D0 +rom_in_var_1: equ 0x213A +rom_in_var_2: equ 0x2148 +rom_in_var_3: equ 0x215E +rom_in_var_4: equ 0x2161 +rom_in_var_5: equ 0x2174 +rom_in_var_6: equ 0x219B +rom_indexer: equ 0x16DC +rom_indexer_1: equ 0x16DB +rom_init_chan: equ 0x15AF +rom_init_strm: equ 0x15C6 +rom_input: equ 0x2089 +rom_input_1: equ 0x2096 +rom_input_2: equ 0x20AD +rom_input_ad: equ 0x15E6 +rom_int: equ 0x36AF +rom_int_case: equ 0x3483 +rom_int_exp1: equ 0x2ACC +rom_int_exp2: equ 0x2ACD +rom_int_fetch: equ 0x2D7F +rom_int_store: equ 0x2D8E +rom_int_to_fp: equ 0x2D3B +rom_ix_end: equ 0x3290 +rom_jump: equ 0x3686 +rom_jump_2: equ 0x3687 +rom_jump_true: equ 0x368F +rom_k_8_and_9: equ 0x0382 +rom_k_@_char: equ 0x03B2 +rom_k_ch_set: equ 0x02D1 +rom_k_decode: equ 0x0333 +rom_k_digit: equ 0x0367 +rom_k_e_let: equ 0x0341 +rom_k_end: equ 0x0308 +rom_k_gra_dgt: equ 0x0389 +rom_k_klc_dgt: equ 0x039D +rom_k_klc_let: equ 0x034F +rom_k_look_up: equ 0x034A +rom_k_main: equ 0x032C +rom_k_new: equ 0x02F1 +rom_k_repeat: equ 0x0310 +rom_k_st_loop: equ 0x02C6 +rom_k_test: equ 0x031E +rom_k_tokens: equ 0x0364 +rom_key_3keys: equ 0x029F +rom_key_bits: equ 0x02A1 +rom_key_chan: equ 0x1113 +rom_key_contr: equ 0x10FA +rom_key_data: equ 0x1105 +rom_key_decode: equ 0x0333 +rom_key_done2: equ 0x111B +rom_key_done: equ 0x02AB +rom_key_flag: equ 0x10F4 +rom_key_input: equ 0x10A8 +rom_key_int: equ 0x0048 +rom_key_line: equ 0x0296 +rom_key_m_cl: equ 0x10DB +rom_key_mode: equ 0x10E6 +rom_key_next: equ 0x110D +rom_key_scan: equ 0x028E +rom_key_test: equ 0x031E +rom_keyboard: equ 0x02BF +rom_l_add_string: equ 0x2BAF +rom_l_char: equ 0x2B3E +rom_l_delete_string: equ 0x2B72 +rom_l_each_ch: equ 0x2B0B +rom_l_enter: equ 0x2BA6 +rom_l_exists: equ 0x2B66 +rom_l_first: equ 0x2BEA +rom_l_in_w_or_s: equ 0x2BA3 +rom_l_length: equ 0x2B9B +rom_l_new_string: equ 0x2BC0 +rom_l_no_sp: equ 0x2B0C +rom_l_numeric: equ 0x2B59 +rom_l_single: equ 0x2B4F +rom_l_spaces: equ 0x2B29 +rom_l_string: equ 0x2BC6 +rom_l_test_ch: equ 0x2B1F +rom_last: equ 0x386C +rom_ld_8_bits: equ 0x05CA +rom_ld_block: equ 0x0802 +rom_ld_break: equ 0x056B +rom_ld_bytes: equ 0x0556 +rom_ld_ch_pr: equ 0x07AD +rom_ld_cont_1: equ 0x0819 +rom_ld_cont_2: equ 0x0825 +rom_ld_contrl: equ 0x0808 +rom_ld_data: equ 0x082E +rom_ld_data_1: equ 0x084C +rom_ld_dec: equ 0x05C4 +rom_ld_delay: equ 0x05E9 +rom_ld_edge_1: equ 0x05E7 +rom_ld_edge_2: equ 0x05E3 +rom_ld_flag: equ 0x05B3 +rom_ld_leader: equ 0x0580 +rom_ld_look_h: equ 0x0767 +rom_ld_loop: equ 0x05A9 +rom_ld_marker: equ 0x05C8 +rom_ld_name: equ 0x07A6 +rom_ld_next: equ 0x05C2 +rom_ld_prog: equ 0x0873 +rom_ld_prog_1: equ 0x08AD +rom_ld_sample: equ 0x05ED +rom_ld_start: equ 0x056C +rom_ld_sync: equ 0x058F +rom_ld_type: equ 0x078A +rom_ld_verify: equ 0x05BD +rom_ld_wait: equ 0x0574 +rom_len: equ 0x3674 +rom_less_0: equ 0x3506 +rom_less_mask: equ 0x328A +rom_let: equ 0x2AFF +rom_line_ad_1: equ 0x1974 +rom_line_addr: equ 0x196E +rom_line_draw: equ 0x2477 +rom_line_end: equ 0x1BB3 +rom_line_new: equ 0x1B9E +rom_line_no: equ 0x1695 +rom_line_no_a: equ 0x1691 +rom_line_run: equ 0x1B8A +rom_line_scan: equ 0x1B17 +rom_line_use: equ 0x1BBF +rom_line_zero: equ 0x168F +rom_list: equ 0x17F9 +rom_list_1: equ 0x17FB +rom_list_2: equ 0x1814 +rom_list_3: equ 0x181A +rom_list_4: equ 0x181F +rom_list_5: equ 0x1822 +rom_list_all: equ 0x1833 +rom_list_all_2: equ 0x1835 +rom_llist: equ 0x17F5 +rom_ln: equ 0x3713 +rom_ln_fetch: equ 0x190F +rom_ln_store: equ 0x191C +rom_loc_mem: equ 0x3406 +rom_log_paren_2_power_a_paren: equ 0x2DC1 +rom_look_p_1: equ 0x1D8B +rom_look_p_2: equ 0x1DA3 +rom_look_prog: equ 0x1D86 +rom_look_vars: equ 0x28B2 +rom_lprint: equ 0x1FC9 +rom_main_1: equ 0x12A9 +rom_main_2: equ 0x12AC +rom_main_3: equ 0x12CF +rom_main_4: equ 0x1303 +rom_main_5: equ 0x133C +rom_main_5_1349: equ 0x1349 +rom_main_6: equ 0x1373 +rom_main_7: equ 0x1376 +rom_main_8: equ 0x1384 +rom_main_9: equ 0x1386 +rom_main_add1: equ 0x157D +rom_main_add2: equ 0x15AB +rom_main_add: equ 0x155D +rom_main_exec: equ 0x12A2 +rom_main_g: equ 0x1313 ; runtime error (error in A) +rom_main_keys: equ 0x0205 +rom_make_expt: equ 0x313B +rom_make_room: equ 0x1655 +rom_mask_int: equ 0x0038 +rom_me_contrl: equ 0x08B6 +rom_me_ent_1: equ 0x093E +rom_me_ent_2: equ 0x0955 +rom_me_ent_3: equ 0x0958 +rom_me_enter: equ 0x092C +rom_me_new_l2: equ 0x08EB +rom_me_new_lp: equ 0x08D2 +rom_me_old_l1: equ 0x08DF +rom_me_old_lp: equ 0x08D7 +rom_me_old_v1: equ 0x0901 +rom_me_old_v2: equ 0x0909 +rom_me_old_v3: equ 0x0912 +rom_me_old_v4: equ 0x091E +rom_me_old_vp: equ 0x08F9 +rom_me_var_l1: equ 0x0921 +rom_me_var_l2: equ 0x0923 +rom_me_var_lp: equ 0x08F0 +rom_mlt_loop: equ 0x3114 +rom_move_fp: equ 0x33C0 +rom_mult_long: equ 0x30F0 +rom_mult_oflw: equ 0x30EF +rom_mult_rslt: equ 0x30EA +rom_multiply: equ 0x30CA +rom_n_mod_m: equ 0x36A0 +rom_n_negtv: equ 0x3705 +rom_near_zero: equ 0x3159 +rom_neg_byte: equ 0x2FAF +rom_neg_test: equ 0x3474 +rom_negate: equ 0x346E +rom_new: equ 0x11B7 +rom_next: equ 0x1DAB +rom_next_1: equ 0x1DE2 +rom_next_2: equ 0x1DE9 +rom_next_2num: equ 0x1C79 +rom_next_char: equ 0x0020 +rom_next_line: equ 0x1BD1 +rom_next_loop: equ 0x1DDA +rom_next_o_1: equ 0x19C7 +rom_next_o_2: equ 0x19CE +rom_next_o_3: equ 0x19D5 +rom_next_o_4: equ 0x19D6 +rom_next_o_5: equ 0x19DB +rom_next_one: equ 0x19B8 +rom_nil_bytes: equ 0x3272 +rom_nmi_vect: equ 0x121C +rom_no_add: equ 0x311B +rom_no_and_no: equ 0x3524 +rom_no_l_eql_etc: equ 0x353B +rom_no_reset: equ 0x0070 +rom_no_rstore: equ 0x31F9 +rom_normalise: equ 0x316C +rom_norml_now: equ 0x3186 +rom_not: equ 0x3501 +rom_not_bin: equ 0x2CB8 +rom_not_used: equ 0x1988 +rom_nu_or_str: equ 0x354E +rom_number: equ 0x18B6 +rom_numeric: equ 0x2D1B +rom_nxt_dgt_1: equ 0x2CDA +rom_nxt_dgt_2: equ 0x2D40 +rom_offst_tbl: equ 0x1A48 +rom_oflow_clr: equ 0x3195 +rom_oflw1_clr: equ 0x3146 +rom_oflw2_clr: equ 0x3151 +rom_one: equ 0x386A +rom_one_shift: equ 0x2FE5 +rom_one_space: equ 0x1652 +rom_op_str_lu: equ 0x177A +rom_open: equ 0x1736 +rom_open_1: equ 0x1756 +rom_open_2: equ 0x175D +rom_open_3: equ 0x1767 +rom_open_end: equ 0x178B +rom_open_k: equ 0x1781 +rom_open_p: equ 0x1789 +rom_open_s: equ 0x1785 +rom_or: equ 0x351B +rom_other_str: equ 0x35B7 +rom_out: equ 0x1E7A +rom_out_c_1: equ 0x18F3 +rom_out_c_2: equ 0x1909 +rom_out_ch_1: equ 0x195A +rom_out_ch_2: equ 0x1968 +rom_out_ch_3: equ 0x196C +rom_out_char: equ 0x1937 +rom_out_code: equ 0x15EF +rom_out_curs: equ 0x18E1 +rom_out_flash: equ 0x18C1 +rom_out_line1: equ 0x1865 +rom_out_line2: equ 0x187D +rom_out_line3: equ 0x1881 +rom_out_line4: equ 0x1894 +rom_out_line5: equ 0x18A1 +rom_out_line6: equ 0x18B4 +rom_out_line: equ 0x1855 +rom_out_num_1: equ 0x1A1B +rom_out_num_2: equ 0x1A28 +rom_out_num_3: equ 0x1A30 +rom_out_num_4: equ 0x1A42 +rom_out_sp_1: equ 0x192B +rom_out_sp_2: equ 0x1925 +rom_out_sp_no: equ 0x192A +rom_p_beep: equ 0x1AE3 +rom_p_border: equ 0x1AF5 +rom_p_bright: equ 0x1AEE +rom_p_cat: equ 0x1B14 +rom_p_circle: equ 0x1AE7 +rom_p_clear: equ 0x1ABB +rom_p_close: equ 0x1B02 +rom_p_cls: equ 0x1ABE +rom_p_cont: equ 0x1AB8 +rom_p_copy: equ 0x1AD6 +rom_p_data: equ 0x1ACC +rom_p_def_fn: equ 0x1AF9 +rom_p_dim: equ 0x1AA2 +rom_p_draw: equ 0x1AD2 +rom_p_erase: equ 0x1B10 +rom_p_flash: equ 0x1AED +rom_p_for: equ 0x1A90 +rom_p_format: equ 0x1B06 +rom_p_go_sub: equ 0x1A86 +rom_p_go_to: equ 0x1A7D +rom_p_if: equ 0x1A81 +rom_p_ink: equ 0x1AEB +rom_p_input: equ 0x1A9F +rom_p_int_sto: equ 0x2D8C +rom_p_inverse: equ 0x1AEF +rom_p_let: equ 0x1A7A +rom_p_list: equ 0x1AAE +rom_p_llist: equ 0x1ADC +rom_p_load: equ 0x1AE0 +rom_p_lprint: equ 0x1AD9 +rom_p_merge: equ 0x1AE2 +rom_p_move: equ 0x1B0A +rom_p_new: equ 0x1AA8 +rom_p_next: equ 0x1A98 +rom_p_open: equ 0x1AFC +rom_p_out: equ 0x1AF1 +rom_p_over: equ 0x1AF0 +rom_p_paper: equ 0x1AEC +rom_p_pause: equ 0x1AC5 +rom_p_plot: equ 0x1AC1 +rom_p_poke: equ 0x1AB1 +rom_p_print: equ 0x1A9C +rom_p_random: equ 0x1AB5 +rom_p_read: equ 0x1AC9 +rom_p_rem: equ 0x1AA5 +rom_p_restore: equ 0x1ACF +rom_p_return: equ 0x1A8D +rom_p_run: equ 0x1AAB +rom_p_save: equ 0x1ADF +rom_p_stop: equ 0x1A8A +rom_p_verify: equ 0x1AE1 +rom_pass_by: equ 0x1E39 +rom_pause: equ 0x1F3A +rom_pause_1: equ 0x1F3D +rom_pause_2: equ 0x1F49 +rom_pause_end: equ 0x1F4F +rom_peek: equ 0x34AC +rom_perm: equ 0x1C96 +rom_pf_all_9: equ 0x2EB8 +rom_pf_bits: equ 0x2E7B +rom_pf_bytes: equ 0x2E8A +rom_pf_count: equ 0x2F2D +rom_pf_dc_out: equ 0x2F5E +rom_pf_dec_0s: equ 0x2F64 +rom_pf_digits: equ 0x2EA1 +rom_pf_e_frmt: equ 0x2F6C +rom_pf_e_pos: equ 0x2F83 +rom_pf_e_sbrn: equ 0x2F4A +rom_pf_e_sign: equ 0x2F85 +rom_pf_fr_dgt: equ 0x2EEC +rom_pf_fr_exx: equ 0x2EEF +rom_pf_fractn: equ 0x2ECF +rom_pf_frn_lp: equ 0x2EDF +rom_pf_insert: equ 0x2EA9 +rom_pf_large: equ 0x2E56 +rom_pf_loop: equ 0x2E01 +rom_pf_medium: equ 0x2E6F +rom_pf_more: equ 0x2ECB +rom_pf_negtve: equ 0x2DF2 +rom_pf_not_e: equ 0x2F46 +rom_pf_out_dt: equ 0x2F59 +rom_pf_out_lp: equ 0x2F52 +rom_pf_postve: equ 0x2DF8 +rom_pf_r_back: equ 0x2F25 +rom_pf_rnd_lp: equ 0x2F18 +rom_pf_round: equ 0x2F0C +rom_pf_save: equ 0x2E1E +rom_pf_small: equ 0x2E24 +rom_pf_test_2: equ 0x2EB3 +rom_pixel_add: equ 0x22AA +rom_pl_tst_in: equ 0x22FD +rom_plot: equ 0x22DC +rom_plot_22df: equ 0x22DF ; secondary entry to PLOT +rom_plot_end: equ 0x2303 +rom_plot_loop: equ 0x22F0 +rom_plot_sub: equ 0x22E5 ; the plot subroutine (coords in BC) +rom_po_1_oper: equ 0x0A7A +rom_po_2_oper: equ 0x0A75 +rom_po_able: equ 0x0AD9 +rom_po_any: equ 0x0B24 +rom_po_at_err: equ 0x0AAC +rom_po_at_set: equ 0x0ABF +rom_po_attr: equ 0x0BDB +rom_po_attr_1: equ 0x0BFA +rom_po_attr_2: equ 0x0C08 +rom_po_back_1: equ 0x0A23 +rom_po_back_2: equ 0x0A38 +rom_po_back_3: equ 0x0A3A +rom_po_change: equ 0x0A80 +rom_po_char: equ 0x0B65 +rom_po_char_2: equ 0x0B6A +rom_po_char_3: equ 0x0B76 +rom_po_comma: equ 0x0A5F +rom_po_cont: equ 0x0A87 +rom_po_each: equ 0x0C22 +rom_po_enter: equ 0x0A4F +rom_po_f_pr: equ 0x0B1D +rom_po_fetch: equ 0x0B03 +rom_po_fill: equ 0x0AC3 +rom_po_gr_1: equ 0x0B38 +rom_po_gr_2: equ 0x0B3E +rom_po_gr_3: equ 0x0B4C +rom_po_msg: equ 0x0C0A +rom_po_quest: equ 0x0A69 +rom_po_right: equ 0x0A3D +rom_po_save: equ 0x0C3B +rom_po_scr: equ 0x0C55 +rom_po_scr_2: equ 0x0C88 +rom_po_scr_3: equ 0x0CD2 +rom_po_scr_3a: equ 0x0CF0 +rom_po_scr_4: equ 0x0D02 +rom_po_scr_4a: equ 0x0D1C +rom_po_scr_4b: equ 0x0D2D +rom_po_search: equ 0x0C41 +rom_po_space: equ 0x0AD0 +rom_po_st_e: equ 0x0AF0 +rom_po_st_pr: equ 0x0AFC +rom_po_step: equ 0x0C44 +rom_po_store: equ 0x0ADC +rom_po_t: equ 0x0B5F +rom_po_t_and_udg: equ 0x0B52 +rom_po_t_udg: equ 0x0B52 +rom_po_tab: equ 0x0AC2 +rom_po_table: equ 0x0C14 +rom_po_tokens: equ 0x0C10 +rom_po_tr_sp: equ 0x0C35 +rom_po_tv_1: equ 0x0A7D +rom_po_tv_2: equ 0x0A6D +rom_point_lp: equ 0x22D4 +rom_point_sub: equ 0x22CB +rom_point_sub_22ce: equ 0x22CE ; secondary entry to POINT-SUB +rom_pointers: equ 0x1664 +rom_poke: equ 0x1E80 +rom_pr_all: equ 0x0B7F ; "print all characters" +rom_pr_all_1: equ 0x0B93 +rom_pr_all_2: equ 0x0BA4 +rom_pr_all_3: equ 0x0BB6 +rom_pr_all_4: equ 0x0BB7 +rom_pr_all_5: equ 0x0BC1 +rom_pr_all_6: equ 0x0BD3 +rom_pr_at_tab: equ 0x201E +rom_pr_end_z: equ 0x2045 +rom_pr_item_1: equ 0x1FFC +rom_pr_item_2: equ 0x200E +rom_pr_item_3: equ 0x2024 +rom_pr_posn_1: equ 0x204E +rom_pr_posn_2: equ 0x2061 +rom_pr_posn_3: equ 0x2067 +rom_pr_posn_4: equ 0x206E +rom_pr_st_end: equ 0x2048 +rom_pr_string: equ 0x203C ; print a string (start in DE, count in BC) +rom_prb_bytes: equ 0x0EE7 +rom_prep_add: equ 0x2F9B +rom_prep_m_or_d: equ 0x30C0 +rom_print: equ 0x1FCD +rom_print_1: equ 0x1FCF +rom_print_2: equ 0x1FDF +rom_print_3: equ 0x1FE5 +rom_print_4: equ 0x1FF2 +rom_print_a: equ 0x0010 +rom_print_a_2: equ 0x15F2 +rom_print_cr: equ 0x1FF5 +rom_print_fp: equ 0x2DE3 ; print a floating point number +rom_print_out: equ 0x09F4 +rom_print_string: equ 0x203C +rom_ptr_done: equ 0x167F +rom_ptr_next: equ 0x166B +rom_r_i_store: equ 0x365F +rom_ram_check: equ 0x11DA +rom_ram_done: equ 0x11EF +rom_ram_fill: equ 0x11DC +rom_ram_read: equ 0x11E2 +rom_ram_set: equ 0x1219 +rom_rand_1: equ 0x1E5A +rom_randomize: equ 0x1E4F +rom_re_entry: equ 0x3365 +rom_re_st_two: equ 0x3293 +rom_re_stack: equ 0x3297 +rom_read: equ 0x1DED +rom_read_1: equ 0x1E0A +rom_read_2: equ 0x1E1E +rom_read_3: equ 0x1DEC +rom_read_in: equ 0x3645 +rom_rec_edit: equ 0x16D4 +rom_reclaim_1: equ 0x19E5 +rom_reclaim_2: equ 0x19E8 +rom_rem: equ 0x1BB2 +rom_remove_fp: equ 0x11A7 +rom_report_0: equ 0x1BB0 +rom_report_1: equ 0x1DD8 +rom_report_2: equ 0x1C2E +rom_report_2a: equ 0x0670 +rom_report_3: equ 0x2A20 +rom_report_4: equ 0x1F15 +rom_report_5: equ 0x0C86 +rom_report_6: equ 0x31AD +rom_report_6b: equ 0x3703 +rom_report_7: equ 0x1F36 +rom_report_8: equ 0x15E4 +rom_report_a: equ 0x34E7 +rom_report_ab: equ 0x371A +rom_report_b: equ 0x046C +rom_report_bb: equ 0x1E9F +rom_report_bc: equ 0x24F9 +rom_report_bd: equ 0x35DC +rom_report_c: equ 0x1C8A +rom_report_cb: equ 0x21CE +rom_report_d: equ 0x0D00 +rom_report_da: equ 0x0552 +rom_report_dc: equ 0x0F0A +rom_report_e: equ 0x1E08 +rom_report_fa: equ 0x0642 +rom_report_fb: equ 0x1765 +rom_report_h: equ 0x21D4 +rom_report_i: equ 0x1D84 +rom_report_j: equ 0x15C4 +rom_report_k: equ 0x2244 +rom_report_l: equ 0x1B7B +rom_report_m: equ 0x1EDA +rom_report_n: equ 0x1BEC +rom_report_oa: equ 0x160E +rom_report_ob: equ 0x1725 +rom_report_p: equ 0x2812 +rom_report_q: equ 0x288B +rom_report_r: equ 0x0806 +rom_reserve: equ 0x169E +rom_reset: equ 0x0066 +rom_rest_run: equ 0x1E45 +rom_restk_sub: equ 0x3296 +rom_restore: equ 0x1E42 +rom_result_ok: equ 0x370C +rom_return: equ 0x1F23 +rom_rom_trap: equ 0x1708 +rom_rpt_mesgs: equ 0x1391 +rom_rs_nrmlse: equ 0x32B1 +rom_rs_store: equ 0x32BD +rom_rslt_zero: equ 0x370E +rom_rstk_loop: equ 0x32B2 +rom_run: equ 0x1EA1 +rom_s_2_coord: equ 0x2522 +rom_s_alphnum: equ 0x2684 +rom_s_attr: equ 0x2672 +rom_s_attr_s: equ 0x2580 +rom_s_attr_s_2583: equ 0x2583 ; secondary entry to S-ATTR-S +rom_s_bracket: equ 0x25E8 +rom_s_cont_1: equ 0x26DD +rom_s_cont_2: equ 0x2712 +rom_s_cont_3: equ 0x2713 +rom_s_decimal: equ 0x268D +rom_s_fn: equ 0x25F5 +rom_s_fn_sbrn: equ 0x27BD +rom_s_ik_dollar_stk: equ 0x2660 +rom_s_ink_dollar_en: equ 0x2665 +rom_s_inkey_dollar: equ 0x2634 +rom_s_letter: equ 0x26C9 +rom_s_loop: equ 0x2734 +rom_s_loop_1: equ 0x24FF +rom_s_loopend: equ 0x2770 +rom_s_negate: equ 0x26DF +rom_s_next: equ 0x2790 +rom_s_no_to_string: equ 0x2707 +rom_s_not_and: equ 0x2788 +rom_s_numeric: equ 0x26C3 +rom_s_opertr: equ 0x2723 +rom_s_pi: equ 0x2627 +rom_s_pi_end: equ 0x2630 +rom_s_point: equ 0x267B +rom_s_push_po: equ 0x270D +rom_s_q_again: equ 0x25BE +rom_s_q_copy: equ 0x25CB +rom_s_q_prms: equ 0x25D9 +rom_s_quote: equ 0x25B3 +rom_s_quote_s: equ 0x250F +rom_s_rnd: equ 0x25F8 +rom_s_rnd_end: equ 0x2625 +rom_s_rport_c2: equ 0x2761 +rom_s_rport_c: equ 0x252D +rom_s_runtest: equ 0x2764 +rom_s_sc_mtch: equ 0x255A +rom_s_sc_rows: equ 0x255D +rom_s_scr_nxt: equ 0x2573 +rom_s_scr_sto: equ 0x257D +rom_s_screen_dollar: equ 0x2668 +rom_s_scrn_dollar_s: equ 0x2535 +rom_s_scrn_lp: equ 0x254F +rom_s_scrn_s: equ 0x2535 +rom_s_scrn_s_2538: equ 0x2538 ; secondary entry to S-SCRN$-S +rom_s_sd_skip: equ 0x26B6 +rom_s_stk_dec: equ 0x26B5 +rom_s_stk_lst: equ 0x274C +rom_s_string: equ 0x25DB +rom_s_syntest: equ 0x275B +rom_s_tighter: equ 0x2773 +rom_s_u_plus: equ 0x25AF +rom_sa_1_sec: equ 0x0991 +rom_sa_8_bits: equ 0x0525 +rom_sa_all: equ 0x075A ; tape save/load +rom_sa_bit_1: equ 0x0514 +rom_sa_bit_2: equ 0x0511 +rom_sa_blank: equ 0x0629 +rom_sa_bytes: equ 0x04C2 +rom_sa_code: equ 0x06C3 +rom_sa_code_1: equ 0x06E1 +rom_sa_code_2: equ 0x06F0 +rom_sa_code_3: equ 0x06F5 +rom_sa_code_4: equ 0x06F9 +rom_sa_contrl: equ 0x0970 +rom_sa_data: equ 0x0652 +rom_sa_data_1: equ 0x0692 +rom_sa_delay: equ 0x053C +rom_sa_flag: equ 0x04D0 +rom_sa_leader: equ 0x04D8 +rom_sa_line: equ 0x0716 +rom_sa_line_1: equ 0x0723 +rom_sa_loop: equ 0x04FE +rom_sa_loop_p: equ 0x0505 +rom_sa_name: equ 0x064B +rom_sa_null: equ 0x0644 +rom_sa_or_ld_end: equ 0x0554 +rom_sa_or_ld_ret: equ 0x053F +rom_sa_out: equ 0x051C +rom_sa_parity: equ 0x050E +rom_sa_scr_dollar: equ 0x06A0 +rom_sa_set: equ 0x051A +rom_sa_space: equ 0x0621 +rom_sa_start: equ 0x0507 +rom_sa_sync_1: equ 0x04EA +rom_sa_sync_2: equ 0x04F2 +rom_sa_type_0: equ 0x073A +rom_sa_type_3: equ 0x0710 +rom_sa_v_new: equ 0x0685 +rom_sa_v_old: equ 0x0672 +rom_sa_v_type: equ 0x068F +rom_save_etc: equ 0x0605 +rom_scan_ent: equ 0x336C +rom_scan_func: equ 0x2596 +rom_scan_loop: equ 0x1B52 +rom_scanning: equ 0x24FB +rom_scrl_mssg: equ 0x0CF8 +rom_sec_plus: equ 0x3575 +rom_secnd_low: equ 0x356B +rom_separator: equ 0x1B6F +rom_series_xx: equ 0x3449 +rom_set_de: equ 0x1195 +rom_set_hl: equ 0x1190 +rom_set_min: equ 0x16B0 +rom_set_permanent_colors_0x1CAD: equ 0x1CAD ; special entry point +rom_set_permanent_colors_1CAD: equ 0x1CAD ; special entry point +rom_set_stk: equ 0x16C5 +rom_set_work: equ 0x16BF +rom_sf_arg_lp: equ 0x2843 +rom_sf_arg_vl: equ 0x2852 +rom_sf_argmt1: equ 0x2802 +rom_sf_argmts: equ 0x27D9 +rom_sf_brkt_1: equ 0x27D0 +rom_sf_brkt_2: equ 0x27E4 +rom_sf_cp_def: equ 0x2814 +rom_sf_flag_6: equ 0x27E9 +rom_sf_fnd_df: equ 0x2808 +rom_sf_not_fd: equ 0x2825 +rom_sf_r_br_2: equ 0x2885 +rom_sf_rprt_c: equ 0x27E6 +rom_sf_run: equ 0x27F7 +rom_sf_syn_en: equ 0x27F4 +rom_sf_value: equ 0x288D +rom_sf_values: equ 0x2831 +rom_sfa_cp_vr: equ 0x296B +rom_sfa_end: equ 0x2991 +rom_sfa_loop: equ 0x295A +rom_sfa_match: equ 0x2981 +rom_sgn: equ 0x3492 +rom_shift_fp: equ 0x2FDD +rom_shift_len: equ 0x3055 +rom_shift_one: equ 0x316E +rom_sign_done: equ 0x2CFE +rom_sign_flag: equ 0x2CF2 +rom_sign_to_c: equ 0x3507 +rom_sin: equ 0x37B5 +rom_skip_cons: equ 0x33F7 +rom_skip_next: equ 0x33F8 +rom_skip_over: equ 0x007D +rom_skip_zero: equ 0x315E +rom_skips: equ 0x0090 +rom_sl_define: equ 0x2A94 +rom_sl_over: equ 0x2AA8 +rom_sl_rpt_c: equ 0x2A7A +rom_sl_second: equ 0x2A81 +rom_sl_store: equ 0x2AAD +rom_slicing: equ 0x2A52 +rom_small: equ 0x37F8 +rom_spare: equ 0x386E +rom_sqr: equ 0x384A +rom_st_e_part: equ 0x2CFF +rom_st_mem_xx: equ 0x342D +rom_stack_a: equ 0x2D28 +rom_stack_bc: equ 0x2D2B ; stack BC register pair +rom_stack_fetch: equ 0x2BF1 ; get last value from calculator stack +rom_stack_num: equ 0x33B4 +rom_start: equ 0x0000 +rom_start_new: equ 0x11CB +rom_stk_code: equ 0x3671 +rom_stk_const: equ 0x33C8 +rom_stk_const_xx: equ 0x341B +rom_stk_data: equ 0x33C6 +rom_stk_digit: equ 0x2D22 +rom_stk_f_arg: equ 0x2951 +rom_stk_fetch: equ 0x2BF1 +rom_stk_pntrs: equ 0x35BF +rom_stk_st_0: equ 0x2AB1 +rom_stk_sto_string: equ 0x2AB2 +rom_stk_store: equ 0x2AB6 +rom_stk_to_a: equ 0x2314 +rom_stk_to_bc: equ 0x2307 +rom_stk_var: equ 0x2996 +rom_stk_zeros: equ 0x33F1 +rom_stmt_l_1: equ 0x1B29 +rom_stmt_loop: equ 0x1B28 +rom_stmt_next: equ 0x1BF4 +rom_stmt_r_1: equ 0x1B7D +rom_stmt_ret: equ 0x1B76 +rom_stop: equ 0x1CEE +rom_str_alter: equ 0x2070 +rom_str_and_no: equ 0x352D +rom_str_data1: equ 0x1727 +rom_str_data: equ 0x171E +rom_str_dollar: equ 0x361F +rom_str_test: equ 0x3588 +rom_strings: equ 0x3559 +rom_strs_add: equ 0x359C +rom_strt_mlt: equ 0x3125 +rom_subn_only: equ 0x31F2 +rom_subtract: equ 0x300F +rom_sv_arrays: equ 0x29AE +rom_sv_ch_add: equ 0x29E0 +rom_sv_close: equ 0x29D8 +rom_sv_comma: equ 0x29C3 +rom_sv_count: equ 0x29E7 +rom_sv_dim: equ 0x2A48 +rom_sv_elem_string: equ 0x2A2C +rom_sv_loop: equ 0x29EA +rom_sv_mult: equ 0x29FB +rom_sv_number: equ 0x2A22 +rom_sv_ptr: equ 0x29C0 +rom_sv_rpt_c: equ 0x2A12 +rom_sv_simple_string: equ 0x29A1 +rom_sv_slice: equ 0x2A45 +rom_sv_slice?: equ 0x2A49 +rom_swap_byte: equ 0x343E +rom_syntax_z: equ 0x2530 +rom_t_expnent: equ 0x326C +rom_t_first: equ 0x3233 +rom_t_gr_zero: equ 0x3221 +rom_t_numeric: equ 0x3252 +rom_t_shift: equ 0x3261 +rom_t_small: equ 0x323F +rom_t_store: equ 0x3267 +rom_t_test: equ 0x325E +rom_tan: equ 0x37DA +rom_tape_msgs: equ 0x09A1 +rom_tbl_addrs: equ 0x32D7 +rom_tbl_of_ops: equ 0x2795 +rom_tbl_priors: equ 0x27B0 +rom_temp_ptr1: equ 0x0077 +rom_temp_ptr2: equ 0x0078 +rom_temps: equ 0x0D4D ; copy the permanent color items to the temporary ones +rom_temps_1: equ 0x0D5B +rom_temps_2: equ 0x0D65 +rom_test_5_sp: equ 0x33A9 +rom_test_char: equ 0x001C +rom_test_neg: equ 0x307C +rom_test_norm: equ 0x3155 +rom_test_room: equ 0x1F05 +rom_test_zero: equ 0x34E9 +rom_tkn_table: equ 0x0095 +rom_to_power: equ 0x3851 +rom_truncate: equ 0x3214 +rom_two_p_1: equ 0x1E8E +rom_two_param: equ 0x1E85 +rom_unstack_z: equ 0x1FC3 +rom_use_252: equ 0x2495 +rom_use_zero: equ 0x1CE6 +rom_usr_no: equ 0x34B3 +rom_usr_range: equ 0x34D3 +rom_usr_stack: equ 0x34E4 +rom_usr_string: equ 0x34BC +rom_v_80_byte: equ 0x2932 +rom_v_char: equ 0x28D4 +rom_v_each: equ 0x2900 +rom_v_end: equ 0x294B +rom_v_found_1: equ 0x293E +rom_v_found_2: equ 0x293F +rom_v_get_ptr: equ 0x2929 +rom_v_matches: equ 0x2912 +rom_v_next: equ 0x292A +rom_v_pass: equ 0x2943 +rom_v_rport_c: equ 0x360C +rom_v_run: equ 0x28FD +rom_v_run_or_syn: equ 0x28EF +rom_v_spaces: equ 0x2913 +rom_v_str_var: equ 0x28DE +rom_v_syntax: equ 0x2934 +rom_v_test_fn: equ 0x28E3 +rom_val_dollar: equ 0x35DE +rom_val_fet_1: equ 0x1C56 +rom_val_fet_2: equ 0x1C59 +rom_valid: equ 0x371C +rom_var_a_1: equ 0x1C22 +rom_var_a_2: equ 0x1C30 +rom_var_a_3: equ 0x1C46 +rom_vr_cont_1: equ 0x07E9 +rom_vr_cont_2: equ 0x07F4 +rom_vr_cont_3: equ 0x0800 +rom_vr_contrl: equ 0x07CB +rom_wait_key1: equ 0x15DE +rom_wait_key: equ 0x15D4 +rom_x_large: equ 0x326D +rom_x_neg: equ 0x36B7 +rom_xis0: equ 0x385D +rom_yneg: equ 0x37A8 +rom_zero_rslt: equ 0x315D +rom_zeros_4_or_5: equ 0x2FFB +rom_zplus: equ 0x37A1 +rom_zx81_name: equ 0x04AA + +; ZX Spectrum 128K + +ROM_SWAP: equ 0x5B00 ; 128K paging subroutine in RAM + +rom_128_new: equ 0x0057 diff --git a/inc/system_variables.inc b/inc/system_variables.inc new file mode 100644 index 0000000..bd7fbd3 --- /dev/null +++ b/inc/system_variables.inc @@ -0,0 +1,309 @@ +; ZX Spectrum system variables + +; By Marcos Cruz (programandala.net) + +; Last modified: 201803231837 +; See change log at the end of the file + +; ============================================================== +; ZX Spectrum +3 + +sys_nmiadd: equ 23728 ; (2) + +; The system variable at 5CB0h (23728) was documented on previous models of the +; Spectrum as 'Not used'. It is now used on the +3 as an NMI jump vector. If an +; NMI occurs, this address is checked. If it contains a 0, then no action is +; taken. However, for any other (non-zero) value, a jump will be made to the +; address given by this variable. NMIs must not occur while the disk system is +; active. + +SWAP_ROM: equ 23296 ; (0x5B00) (R16) + ; Paging subroutine. +sys_stoo: equ 23312 ; (0x5B10) (R17) + ; Paging subroutine. Entered with + ; interrupts already disabled and AF, BC + ; on the stack. +sys_younger: equ 23329 ; (0x5B21) (R9) + ; Paging subroutine. +sys_regnuoy: equ 23338 ; (0x5B2A) (R16) + ; Paging subroutine. +sys_onerr: equ 23354 ; (0x5B3A) (R24) + ; Paging subroutine. +sys_oldhl: equ 23378 ; (0x5B52) (X2) + ; Temporary register store while + ; switching ROMs. +sys_oldbc: equ 23380 ; (0x5B54) (X2) + ; Temporary register store while + ; switching ROMs. +sys_oldaf: equ 23382 ; (0x5B56) (X2) + ; Temporary register store while + ; switching ROMs. +sys_target: equ 23384 ; (0x5B58) (N2) + ; Subroutine address in ROM 3. +sys_retaddr: equ 23386 ; (0x5B5A) (X2) + ; Return address in ROM 1. +sys_bankm: equ 23388 ; (0x5B5C) (X1) + ; Copy of last byte output to I/O + ; port 7FFDh (32765). This port + ; is used to control the RAM + ; paging (bits 0..2: page 0..7), + ; screen selection (bit 3: + ; 0=normal, 1=shadow), + ; 'horizontal' ROM switch (bit 4: + ; 0=128k editor, 1=48k BASIC) and + ; added I/O disabling (bit 5). + ; the This byte must be kept up + ; to date with the last value + ; output to the port if + ; interrupts are enabled. +sys_ramrst: equ 23389 ; (0x5B5D) (X1) + ; RST 8 instruction. Used by ROM 1 to + ; report old errors to ROM 3. +sys_ramerr: equ 23390 ; (0x5B5E) (N1) + ; Error number passed from ROM 1 to ROM + ; 3. Also used by SAVE/LOAD as temporary + ; drive store. +sys_baud: equ 23391 ; (0x5B5F) (2) + ; RS232 bit period in T states/26. Set + ; by FORMAT LINE. +sys_serfl: equ 23393 ; (0x5B61) (N2) + ; Second-character-received-flag, and + ; data. +sys_col: equ 23395 ; (0x5B63) (N1) + ; Current column from 1 to width. +sys_width: equ 23396 ; (0x5B64) (1) + ; Paper column width. Defaults to 80. +sys_tvpars: equ 23397 ; (0x5B65) (1) + ; Number of inline parameters expected + ; by RS232 + +sys_flags3: equ 23398 ; (0x5B66) (1) + ; Various flags. Bits 0, 1, 6 and 7 + ; unlikely to be useful. Bit 2 is set + ; when tokens are to be expanded on + ; printing. Bit 3 is set if print output + ; is RS232. The default (at reset) is + ; Centronics. Bit 4 is set if a disk + ; interface is present. Bit 5 is set if + ; drive B: is present. +sys_bank678: equ 23399 ; (0x5B67) (X1) + ; Copy of last byte output to I/O port + ; 1FFDh (8189). This port is used to + ; control the +3 extended RAM and ROM + ; switching (bits 0..2 - if bit 0 is 0 + ; then bit 2 controls the 'vertical' ROM + ; switch 02 and 13), the disk + ; motor (bit 3) and Centronics strobe + ; (bit 4). This byte must be kept up to + ; date with the last value output to the + ; port if interrupts are enabled. +sys_xloc: equ 23400 ; (0x5B68) (N1) + ; Holds X location when using the + ; unexpanded COPY command. +sys_yloc: equ 23401 ; (0x5B69) (N1) + ; Holds Y location when using the + ; unexpanded COPY command. +sys_oldsp: equ 23402 ; (0x5B6A) (X2) + ; Old SP (stack pointer) when TSTACK is + ; in use. +sys_synret: equ 23404 ; (0x5B6C) (X2) + ; Return address for ONERR. +sys_lastv: equ 23406 ; (0x5B6E) (5) + ; Last value printed by calculator. +sys_rcline: equ 23411 ; (0x5B73) (2) + ; Current line being renumbered. +sys_rcstart: equ 23413 ; (0x5B75) (2) + ; Starting line number for + ; renumbering. The default value is 10. +sys_rcstep: equ 23415 ; (0x5B77) (2) + ; Incremental value for renumbering. The + ; default is 10. +sys_loddrv: equ 23417 ; (0x5B79) (1) + ; Holds 'T' if LOAD, VERIFY, MERGE are + ; from tape, otherwise holds 'A', 'B' + ; or 'M'. +sys_savdrv: equ 23418 ; (0x5B7A) (1) + ; Holds 'T' if SAVE is to tape, + ; otherwise holds 'A', 'B' or 'M'. +sys_dumplf: equ 23419 ; (0x5B7B) (1) + ; Holds the number of 1/216ths user for + ; line feeds in 'COPY EXP'. This is + ; normally set to 9. If problems are + ; experienced fitting a dump onto a + ; sheet of A4 paper, POKE this + ; location with 8. This will reduce the + ; size of the dump and improve the + ; aspect ratio slightly. (The quality of + ; the dump will be marginally degraded, + ; however.) +sys_strip1: equ 23420 ; (0x5B7C) (N8) + ; Stripe one bitmap. +sys_strip2: equ 23428 ; (0x5B84) (N8) + ; Stripe two bitmap. This extends to + ; 5B8Bh (23436). +sys_tstack: equ 23551 ; (0x5BFF) (X115) + ; Temporary stack grows down from + ; here. Used when RAM page 7 is switched + ; in at top of memory (while executing + ; the editor or calling +3DOS). it may + ; safely go down to 5B8Ch (and across + ; STRIP1 and STRIP2 if necessary). This + ; guarantees at least 115 bytes of stack + ; when BASIC calls +3DOS. + +; ============================================================== +; ZX Spectrum 48K + +sys_printer_buffer: equ 23296 ; (0x5B00) + +sys_kstate: equ 23552 ; (n8) Used in reading the keyboard. +sys_kstate.1: equ 23553 +sys_kstate.2: equ 23554 +sys_kstate.3: equ 23555 +sys_kstate.4: equ 23556 +sys_kstate.5: equ 23557 +sys_kstate.6: equ 23558 +sys_kstate.7: equ 23559 +sys_last_k: equ 23560 ; (nl) Stores newly pressed key. +sys_last_key: equ 23560 +sys_repdel: equ 23561 ; (1) Time (in 50ths of a second + ; in 60ths of a second in N. America) that a key must + ; be held down before it repeats. + ; This starts off at 35, but you can POKE in other values. +sys_repper: equ 23562 ; (1) Delay (in 50ths of a second in 60ths of a second + ; in N. America) between successive repeats + ; of a key held down: initially 5. +sys_defadd: equ 23563 ; (n2) Address of arguments of user defined function + ; if one is being evaluated; otherwise 0. +sys_k_data: equ 23565 ; (nl) Stores 2nd byte of colour controls + ; entered from keyboard. +sys_tvdata: equ 23566 ; (n2) Stores bytes of colour, AT and TAB controls + ; going to television. +sys_tvdata2: equ 23567 +sys_tvdata.high: equ 23567 +sys_strms: equ 23568 ; (x38) Addresses of channels attached to streams. +sys_chars: equ 23606 ; (2) 256 less than address of character set +sys_rasp: equ 23608 ; (1) Length of warning buzz. +sys_pip: equ 23609 ; (1) Length of keyboard click. + +; sys_err_nr is pointed by IY + +sys_err_nr: equ 23610 ; (1) 1 less than the report code. + ; Starts off at 255 (for 1) so PEEK 23610 gives 255. +sys_flags: equ 23611 ; (x1) Various flags to control the BASIC system. +sys_tv_flag: equ 23612 ; (x1) Flags associated with the television. +sys_err_sp: equ 23613 ; (x2) Address of item on machine stack + ; to be used as error return. +sys_list_sp: equ 23615 ; (n2) Address of return address from automatic listing. +sys_mode: equ 23617 ; (n1) Specifies K, L, C. E or G cursor. +sys_newppc: equ 23618 ; (2) Line to be jumped to. +sys_nsppc: equ 23620 ; (1) Statement number in line to be jumped to. + ; Poking first NEWPPC and then NSPPC + ; forces a jump to a specified statement in a line. +sys_ppc: equ 23621 ; (2) Line number of statement currently being executed. +sys_subppc: equ 23623 ; (1) Number within line of statement being executed. +sys_bordcr: equ 23624 ; (1) Border colour * 8; also contains the attributes + ; normally used for the lower half of the screen. +sys_e_ppc: equ 23625 ; (2) Number of current line (with program cursor). +sys_vars: equ 23627 ; (x2) Address of variables. +sys_dest: equ 23629 ; (n2) Address of variable in assignment. +sys_chans: equ 23631 ; (x2) Address of channel data. +sys_curchl: equ 23633 ; (x2) Address of information currently being used + ; for input and output. +sys_prog: equ 23635 ; (x2) Address of BASIC program. +sys_nxtlin: equ 23637 ; (x2) Address of next line in program. +sys_datadd: equ 23639 ; (x2) Address of terminator of last DATA item. +sys_e_line: equ 23641 ; (x2) Address of command being typed in. +sys_k_cur: equ 23643 ; (2) Address of cursor. +sys_ch_add: equ 23645 ; (x2) Address of the next character to be interpreted. +sys_x_ptr: equ 23647 ; (2) Address of the character after the ? marker. +sys_worksp: equ 23649 ; (x2) Address of temporary work space. +sys_stkbot: equ 23651 ; (x2) Address of bottom of calculator stack. +sys_stkend: equ 23653 ; (x2) Address of start of spare space. +sys_breg: equ 23655 ; (n1) Calculator's b register. +sys_mem: equ 23656 ; (n2) Address of area used for calculator's memory. + ; (Usually MEMBOT, but not always.) +sys_flags2: equ 23658 ; (1) More flags. +sys_df_sz: equ 23659 ; (x1) The number of lines (including one blank line) + ; in the lower part of the screen. +sys_s_top: equ 23660 ; (2) The number of the top program line in automatic listings. +sys_oldppc: equ 23662 ; (2) Line number to which CONTINUE jumps. +sys_ospcc: equ 23664 ; (1) Number within line of statement + ; to which CONTINUE jumps. +sys_flagx: equ 23665 ; (n1) Various flags. +sys_strlen: equ 23666 ; (n2) Length of string type destination in assignment. +sys_t_addr: equ 23668 ; (n2) Address of next item in syntax table + ; (very unlikely to be useful). +sys_seed: equ 23670 ; (2) The seed for RND. + ; This is the variable that is set by RANDOMIZE. +sys_frames: equ 23672 ; (3) 3 byte (least significant first), frame counter. + ; Incremented every 20ms. +sys_udg: equ 23675 ; (2) Address of 1st user defined graphic. +sys_coords: equ 23677 ; (2) coordinates of last point plotted. +sys_coordx: equ 23677 ; (1) x-coordinate of last point plotted. +sys_coordy: equ 23678 ; (1) y-coordinate of last point plotted. +sys_p_posn: equ 23679 ; (1) 33 column number of printer position +sys_pr_cc: equ 23680 ; (1) Less significant byte of address + ; of next position for LPRINT to print at + ; (in printer buffer). +sys_unused0: equ 23681 ; (1) Not used. +sys_echo_e: equ 23682 ; (2) 33 column number and 24 line number + ; (in lower half) of end of input buffer. +sys_df_cc: equ 23684 ; (2) Address in display file of PRINT position. +sys_dfccl: equ 23686 ; (2) Like DF CC for lower part of screen. +sys_s_posn: equ 23688 ; (x2) 33 column and 24 line number for PRINT position +sys_s_posx: equ 23688 ; (x1) 33 column number for PRINT position +sys_s_posy: equ 23689 ; (x1) 24 line number for PRINT position. +sys_s_posn2: equ 23689 +sys_sposnl: equ 23690 ; (x2) Like S POSN for lower part +sys_scr_ct: equ 23692 ; (1) Counts scrolls: + ; it is always 1 more than the number of scrolls that + ; will be done before stopping with scroll? If you keep + ; poking this with a number bigger than 1 (say 255), the + ; screen will scroll on and on without asking you. +sys_attr_p: equ 23693 ; (1) Permanent current colours, etc + ; (as set up by colour statements). +sys_mask_p: equ 23694 ; (1) Used for transparent colours, etc. + ; Any bit that is 1 shows that the corresponding + ; attribute bit is taken not from ATTR P, but from what + ; is already on the screen. +sys_attr_t: equ 23695 ; (n1) Temporary current colours, etc + ; (as set up by colour items). +sys_mask_t: equ 23696 ; (n1) Like MASK P, but temporary. +sys_p_flag: equ 23697 ; (1) More flags. +sys_membot: equ 23698 ; (n30) Calculator's memory area; + ; used to store numbers that cannot + ; conveniently be put on the calculator stack. +sys_unused1: equ 23728 ; (1) Not used. +sys_unused2: equ 23729 ; (1) Not used. +sys_ramtop: equ 23730 ; (2) Address of last byte of BASIC system area. +sys_p_ramt: equ 23732 ; (2) Address of last byte of physical RAM. +sys_p_ramt.high: equ 23733 + +; ============================================================== +; Change log + +; 2014-12-17: Converted from the HTML version of the ZX Spectrum manual, with +; the help of Vim. Regulary improved afterwards. +; +; 2015-01-12: New: first two ZX Spectrum +3 variables. +; +; 2015-01-15: New: 'sys_flags3'. +; +; 2015-01-16: New: all ZX Spectrum +3 variables. +; +; 2015-05-24: Changes to make the file usable as symbols input file by +; z80dasm. +; +; 2015-09-06: Offset extracted to , +; because the z80dasm disassembler does not accept calculations in its input +; symbols file. +; +; 2016-08-05: Remove useless comment. +; +; 2016-08-15: Add individual bytes of sys_k_state; the second one is addressed +; by TR-DOS 5.03. +; +; 2018-03-09: Add `sys_printer_buffer`. +; +; 2018-03-23: Add `sys_tvdata.high`. diff --git a/inc/trdos.inc b/inc/trdos.inc new file mode 100644 index 0000000..4b725d5 --- /dev/null +++ b/inc/trdos.inc @@ -0,0 +1,29 @@ +; ZX Spectrum TR-DOS + +; By Marcos Cruz (programandala.net) + +; -------------------------------------------------------------- +; History + +; 2016-08-13: Start, from information compiled for Solo Forth +; (http://programandala.net/en.program.solo_forth.html). +; +; 2016-09-01: Add drive step rate variables. +; +; 2016-10-01: Complete the variables and entry points. Identify +; unknown variable 23817. +; +; 2017-02-05: Update name of variable 23766, which is used for +; two different things. Add symbols for error codes. +; +; 2017-02-10: Add synonymous of command 0x0E. Fix typo in +; symbol. Add first draft of sector buffer addresses. +; +; 2017-02-10: Split the file in two: addresses and codes. The +; reason is to be able to use only the addresses as input in +; the TR-DOS disassembled project. + +; -------------------------------------------------------------- + +include "inc/zx_spectrum_trdos_addresses.z80s" +include "inc/zx_spectrum_trdos_codes.z80s" diff --git a/inc/trdos_addresses.inc b/inc/trdos_addresses.inc new file mode 100644 index 0000000..5f55a36 --- /dev/null +++ b/inc/trdos_addresses.inc @@ -0,0 +1,184 @@ +; ZX Spectrum TR-DOS addresses + +; By Marcos Cruz (programandala.net) + +; -------------------------------------------------------------- +; History + +; 2017-02-10: Extract from the main file: entry points, +; variables and sector buffer addresses. +; +; 2017-03-08: Rename `trdos_variable.file_size` to +; `trdos_variable.stream_or_file_size`. +; +; 2017-03-09: Improve name of variable 23814. + +; -------------------------------------------------------------- +; TR-DOS entry points + +trdos_entry.enter_trdos_from_basic: equ 15616 ; 0x3D00 + +trdos_entry.interpret_rem_command: equ 15619 ; 0x3D03 + ; Interpret a TR-DOS REM-command pointed by the system variable CH + ; ADD (23645). + ; + ; XXX REMARK -- The Beta Disk and Beta 128 Disk manuals use address + ; 15363 in the assembly examples, but it's a mistake. In fact 15363 + ; (0x3C03) is out of the range of addresses that make the TR-DOS + ; memory page in (0x3D00..0x3DFF). Probably the confusion was caused + ; by a typo in hex (0x3D03 and 0x3C03). + +trdos_entry.execute_command_in_c: equ 15635 ; 0x3D13 + ; Execute a TR-DOS command, whose code is in the C register. + +entry.init_dos: equ 15649 ; 0x3D21 + +trdos_entry.call_pushed_address: equ 15663 ; 0x3D2F + ; Execute the TR-DOS routine whose address is on top of stack. + +; trdos_entry.XXX equ 15622 ; 0x3D06 +; trdos_entry.XXX equ 15629 ; 0x3D0D +; trdos_entry.XXX equ 15632 ; 0x3D10 +; trdos_entry.XXX equ 15652 ; 0x3D24 + +; -------------------------------------------------------------- +; TR-DOS variables + +trdos_variable.interface_1_zone: equ 23734 +trdos_variable.0xc9: equ 23746 +trdos_variable.unknown_23747: equ 23747 +trdos_variable.drive_a_mode: equ 23752 +trdos_variable.drive_b_mode: equ 23753 +trdos_variable.drive_c_mode: equ 23754 +trdos_variable.drive_d_mode: equ 23755 +trdos_variable.cat_current_sector: equ 23756 +trdos_variable.disk_drive_ready: equ 23757 +trdos_variable.sector_rw_flag: equ 23758 +trdos_variable.stack_sp_copy: equ 23759 +trdos_variable.basic_program_autostart: equ 23761 +trdos_variable.basic_program_autostart.high: equ 23762 +trdos_variable.unknown_23763: equ 23763 +trdos_variable.move_command_deleted_files: equ 23764 +trdos_variable.move_command_first_sector: equ 23765 +trdos_variable.bad_sectors_or_move_command_first_track: equ 23766 +trdos_variable.start_file_parameter_or_number_of_tracks: equ 23767 +trdos_variable.start_file_parameter.high: equ 23768 +trdos_variable.ch_add: equ 23769 +trdos_variable.ch_add.high: equ 23770 +trdos_variable.stream_or_file_size: equ 23771 +trdos_variable.file_size.high: equ 23772 +trdos_variable.filename: equ 23773 +trdos_variable.filename.1: equ 23774 +trdos_variable.filename.2: equ 23775 +trdos_variable.filename.3: equ 23776 +trdos_variable.filename.4: equ 23777 +trdos_variable.filename.5: equ 23778 +trdos_variable.filename.6: equ 23779 +trdos_variable.filename.7: equ 23780 +trdos_variable.file_type: equ 23781 +trdos_variable.file_start: equ 23782 +trdos_variable.file_start.high: equ 23783 +trdos_variable.file_length_in_bytes: equ 23784 +trdos_variable.file_length_in_bytes.high: equ 23785 +trdos_variable.file_length_in_sectors: equ 23786 +trdos_variable.file_first_sector: equ 23787 +trdos_variable.file_first_track: equ 23788 +trdos_variable.c_file_start_or_b_file_size: equ 23789 +trdos_variable.file_2_length_in_bytes: equ 23791 +trdos_variable.file_2_length_in_sectors: equ 23793 +trdos_variable.file_2_first_sector: equ 23794 +trdos_variable.file_2_first_track: equ 23795 +trdos_variable.current_sector: equ 23796 +trdos_variable.current_track: equ 23797 +trdos_variable.current_temporary_drive: equ 23798 +trdos_variable.cleared: equ 23799 +trdos_variable.two_files_drive: equ 23800 +trdos_variable.cat_drive_or_read_verify_flag: equ 23801 +trdos_variable.drive_a_step_rate: equ 23802 +trdos_variable.drive_b_step_rate: equ 23803 +trdos_variable.drive_c_step_rate: equ 23804 +trdos_variable.drive_d_step_rate: equ 23805 +trdos_variable.last_fdc_command: equ 23806 +trdos_variable.sector: equ 23807 +trdos_variable.buffer: equ 23808 +trdos_variable.hl_copy: equ 23810 +trdos_variable.de_copy: equ 23812 +trdos_variable.bytes_compared_by_find_command: equ 23814 +trdos_variable.deleted_files: equ 23815 +trdos_variable.filename_first_char: equ 23816 +trdos_variable.data_file_mode: equ 23817 +trdos_variable.buffer_flag: equ 23820 +trdos_variable.file_number_for_copying: equ 23821 +trdos_variable.command_mode: equ 23822 +trdos_variable.error_code: equ 23823 +trdos_variable.error_code_msb: equ 23824 +trdos_variable.command_line_address: equ 23825 +trdos_variable.err_sp_copy: equ 23827 +trdos_variable.show_screen_flag: equ 23829 +trdos_variable.system_register: equ 23830 +trdos_variable.show_title_flag: equ 23831 +trdos_variable.interface_1_flag: equ 23832 +trdos_variable.default_drive: equ 23833 +trdos_variable.internal_address_of_finishing_proc: equ 23834 +trdos_variable.sp_copy: equ 23836 +trdos_variable.find_command_found_file: equ 23838 +trdos_variable.unknown_23839: equ 23839 +trdos_variable.entered_line_3_first_chars: equ 23840 +trdos_variable.entered_line_3_first_chars.1: equ 23841 +trdos_variable.entered_line_3_first_chars.2: equ 23842 +trdos_variable.move_command_blocks: equ 23843 +trdos_variable.tmp_buffer_address: equ 23845 +trdos_variable.k_channel.output: equ 23846 +trdos_variable.k_channel.input: equ 23848 +trdos_variable.k_channel.id: equ 23850 +trdos_variable.s_channel.output: equ 23851 +trdos_variable.s_channel.input: equ 23853 +trdos_variable.s_channel.id: equ 23855 +trdos_variable.r_channel.output: equ 23856 +trdos_variable.r_channel.input: equ 23858 +trdos_variable.r_channel.input.high: equ 23859 +trdos_variable.r_channel.id: equ 23860 +trdos_variable.p_channel.output: equ 23861 +trdos_variable.p_channel.input: equ 23863 +trdos_variable.p_channel.id: equ 23865 + +; -------------------------------------------------------------- +; TR-DOS sector buffer + +trdos_sector_buffer: equ 0x5D25 + ; 256-byte sector buffer + +; Addresses of the 8th sector of track 0, which contains the +; disk metatada, when it's loaded in the sector buffer: + +trdos_sector_buffer.sectors: equ 0x5D04 + ; 2 B: total number of sectors +trdos_sector_buffer.next_free_sector: equ 0x5E06 + ; 1 B: Next free sector (0 index, 0 default) +trdos_sector_buffer.next_free_track: equ 0x5E07 + ; 1 B: Next free track +trdos_sector_buffer.disk_type: equ 0x5E08 + ; 1 B: disk type: + ; 0x16 - 80 tracks, 2 sides + ; 0x17 - 40 tracks, 2 sides + ; 0x18 - 80 tracks, 1 side + ; 0x19 - 40 tracks, 1 side +trdos_sector_buffer.files: equ 0x5E09 + ; 1 B: Number of files, including the deleted files +trdos_sector_buffer.free_sectors: equ 0x5E0A + ; 2 B: Number of free sectors, depending on the disk type: + ; 2544 = 0x09F0 : 80 tracks, 2 sides + ; 1264 = 0x04F0 - 40 tracks, 2 sides + ; 1264 = 0x04F0 - 80 tracks, 1 side + ; 624 = 0x0270 - 40 tracks, 1 side + ; 2672 = 0x0A70 - 84 tracks, 2 sides (DCU format) + ; 2736 = 0x0AB0 - 86 tracks, 2 sides (DCU format) +trdos_sector_buffer.sectors_per_track: equ 0x5E0C + ; 1 B: Number of sectors per track: + ; If not 0x10, the disk will not be recognized. +trdos_sector_buffer.deleted_files: equ 0x5E19 + ; 1 B: Number of deleted files +trdos_sector_buffer.disc_title: equ 0x5E1A + ; 8 B: Disk title + + diff --git a/inc/trdos_codes.inc b/inc/trdos_codes.inc new file mode 100644 index 0000000..f18b24e --- /dev/null +++ b/inc/trdos_codes.inc @@ -0,0 +1,53 @@ +; ZX Spectrum TR-DOS codes + +; By Marcos Cruz (programandala.net) + +; -------------------------------------------------------------- +; History + +; 2017-02-10: Extracted from the main file: command and error +; codes. + +; -------------------------------------------------------------- +; TR-DOS command codes + +trdos_command.init_interface: equ 0x00 +trdos_command.init_drive: equ 0x01 +trdos_command.seek_track: equ 0x02 +trdos_command.set_sector: equ 0x03 +trdos_command.define_buffer: equ 0x04 +trdos_command.read_sectors: equ 0x05 +trdos_command.write_sectors: equ 0x06 +trdos_command.cat: equ 0x07 +trdos_command.read_file_descriptor: equ 0x08 +trdos_command.write_file_descriptor: equ 0x09 +trdos_command.find_file: equ 0x0A +trdos_command.create_file: equ 0x0B +trdos_command.save_basic_program: equ 0x0C +trdos_command.read_file: equ 0x0E +trdos_command.load_or_verify: equ 0x0E +trdos_command.delete_file: equ 0x12 +trdos_command.copy_from_hl_to_descriptor: equ 0x13 +trdos_command.copy_from_descriptor_to_hl: equ 0x14 +trdos_command.test_track: equ 0x15 +trdos_command.select_bottom_side: equ 0x16 +trdos_command.select_top_side: equ 0x17 +trdos_command.read_system_track: equ 0x18 + +; -------------------------------------------------------------- +; TR-DOS error codes + +trdos_error.no_errors equ 0 +trdos_error.no_files equ 1 +trdos_error.file_exists equ 2 +trdos_error.no_space equ 3 +trdos_error.directory_full equ 4 +trdos_error.record_number_overflow equ 5 +trdos_error.no_disk equ 6 +trdos_error.disk_errors equ 7 +trdos_error.syntax_errors equ 8 +trdos_error.undefined_error equ 9 +trdos_error.stream_already_opened equ 10 +trdos_error.not_disk_file equ 11 +trdos_error.stream_not_open equ 12 +