diff --git a/Components/SDCARD/sd_controller.vhd b/Components/SDCARD/sd_controller.vhd index c6c9b8f..1aae375 100644 --- a/Components/SDCARD/sd_controller.vhd +++ b/Components/SDCARD/sd_controller.vhd @@ -114,6 +114,22 @@ signal address: std_logic_vector(31 downto 0) :=x"00000000"; signal led_on_count : integer range 0 to 200; + -- reg 000 - read/write data + + -- reg 001 - read status + -- bit 7 = block written + -- bit 6 = block read + -- bit 5 = block busy + -- bit 4 = init busy + + -- reg 001 - write control + -- 0 - block_read + -- 1 - block_write + + -- reg 002 - LBA0 + -- reg 003 - LBA1 + -- reg 004 - LBA2 + begin process(n_wr) begin @@ -142,8 +158,8 @@ begin dataOut <= dout when regAddr = "000" - else status when regAddr = "001" - else "00000000"; + else status when regAddr = "001" + else "00000000"; process(n_wr) begin @@ -444,7 +460,7 @@ begin bit_counter := 7; state <= write_block_byte; byte_counter := byte_counter - 1; - sd_write_flag <= not sd_write_flag; + sd_write_flag <= not sd_write_flag; -- now sd_write_flag = host_write_flag end if; end if; diff --git a/Components/USB/ch376s.v b/Components/USB/ch376s.v new file mode 100644 index 0000000..6ff5c55 --- /dev/null +++ b/Components/USB/ch376s.v @@ -0,0 +1,49 @@ +module ch376s +( + // interface + input clk, // system clock + input rd, + input wr, + input reset, + input a0, + + // SPI wires + output sck, // SCK + output sdcs, // SCS + output sdo, // MOSI + input sdi, // MISO + + // data + input [7:0] din, + output [7:0] dout + +); + + wire _ready; + wire [7:0] _dout; + + spi SPI_Master + ( + // Control/Data Signals, + .clk(clk), // FPGA Clock + .reset (reset), + .ready (_ready), + + // TX (MOSI) Signals + .din(din), // Byte to transmit on MOSI + .wr (wr), // Data Valid Pulse with i_TX_Byte + + // RX (MISO) Signals + .dout(_dout), // Byte received on MISO + .rd (rd), + + // SPI Interface + .sck(sck), + .sdi(sdi), + .sdo(sdo), + .sdcs (sdcs) + ); + + assign dout=rd ? (a0 ? {7'b0000000,_ready} : _dout) : 8'bXXXXXXXX; + +endmodule \ No newline at end of file diff --git a/Components/USB/spi_master_simple.v b/Components/USB/spi_master_simple.v new file mode 100644 index 0000000..3e6cd7c --- /dev/null +++ b/Components/USB/spi_master_simple.v @@ -0,0 +1,89 @@ +// part of NeoGS project (c) 2007-2008 NedoPC +// + +// SPI mode 0 8-bit master module +// +// short diagram for speed=0 (Fclk/Fspi=2, no rdy shown) +// +// clk: ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ (positive edges) +// counter: 00|00|00|10|11|12|13|14|15|16|17|18|19|1A|1B|1C|1D|1E|1F|00|00|00 // internal! +// sck: ___________/``\__/``\__/``\__/``\__/``\__/``\__/``\__/``\_______ +// sdo: --------< do7 | do6 | do5 | do4 | do3 | do2 | do1 | do0 >------- +// sdi: --------< di7 | di6 | di5 | di4 | di3 | di2 | di1 | di0 >------- +// bsync: ________/`````\_________________________________________________ +// start: _____/``\_______________________________________________________ +// din: ------------------------------------------------------------ +// dout: old old old old old old old old old old old old old | new new new +// +// data on sdo must be latched by slave on rising sck edge. data on sdo changes on falling edge of sck +// +// data from sdi is latched by master on positive edge of sck, while slave changes it on falling edge. +// WARNING: slave must emit valid di7 bit BEFORE first pulse on sck! +// +// start is synchronous pulse, which starts all transfer and also latches din data on the same clk edge +// as it is registered high. start can be given anytime (only when speed=0), +// so it is functioning then as synchronous reset. when speed!=0, there is global enable for majority of +// flipflops in the module, so start can't be accepted at any time +// +// dout updates with freshly received data at the clk edge in which sck goes high for the last time, thus +// latching last bit on sdi. +// +// sdo emits last bit shifted out after the transfer end + +module spi +( + // interface + input clk, // system clock + input rd, + input wr, + input reset, + + // SPI wires + output sck, // SCK + output sdcs, // SCS + output reg sdo, // MOSI + input sdi, // MISO + + // data + input [7:0] din, + output reg [7:0] dout, + + // output + output ready // start strobe, 1 clock length +); + +reg [4:0] counter; + +assign sck = counter[0]; +assign sdcs = 1'b0; // slave always selected +assign ready = counter[4]; // 0 - transmission in progress + +always @(posedge clk) begin + reg [7:0] shift; + + if (reset) begin + counter[4] <= 5'b0; + end + else if (wr) begin + counter <= 5'b0; + sdo <= din[7]; + shift[7:1] <= din[6:0]; + end + else if (!ready) begin + counter <= counter + 5'd1; + + // shift in (rising edge of SCK) + if (!sck) begin + shift[0] <= sdi; + if (&counter[3:1]) dout <= {shift[7:1], sdi}; + end + + // shift out (falling edge of sck) + if (sck) begin + sdo <= shift[7]; + shift[7:1] <= shift[6:0]; // last bit remains after end of exchange + end + end +end + +endmodule \ No newline at end of file diff --git a/MicrocomputerZ80CPM.vhd b/MicrocomputerZ80CPM.vhd index e694b62..d36a901 100644 --- a/MicrocomputerZ80CPM.vhd +++ b/MicrocomputerZ80CPM.vhd @@ -54,11 +54,16 @@ entity MicrocomputerZ80CPM is ps2Clk : in std_logic; ps2Data : in std_logic; - sdCS : out std_logic; + sdCS : out std_logic; sdMOSI : out std_logic; sdMISO : in std_logic; sdSCLK : out std_logic; - driveLED : out std_logic :='1' + driveLED : out std_logic :='1'; + + usbCS : out std_logic; + usbMOSI : out std_logic; + usbMISO : in std_logic; + usbSCLK : out std_logic ); end MicrocomputerZ80CPM; @@ -75,6 +80,7 @@ architecture struct of MicrocomputerZ80CPM is signal internalRam2DataOut : std_logic_vector(7 downto 0); signal interface1DataOut : std_logic_vector(7 downto 0); signal interface2DataOut : std_logic_vector(7 downto 0); + signal ch376sDataOut : std_logic_vector(7 downto 0); signal sdCardDataOut : std_logic_vector(7 downto 0); signal n_memWR : std_logic :='1'; @@ -95,6 +101,7 @@ architecture struct of MicrocomputerZ80CPM is signal n_basRomCS : std_logic :='1'; signal n_interface1CS : std_logic :='1'; signal n_interface2CS : std_logic :='1'; + signal n_ch376sCS : std_logic :='1'; signal n_sdCardCS : std_logic :='1'; signal serialClkCount : std_logic_vector(15 downto 0); @@ -107,6 +114,28 @@ architecture struct of MicrocomputerZ80CPM is --CPM signal n_RomActive : std_logic := '0'; +component ch376s is + port ( + -- interface + clk : in std_logic; + rd : in std_logic; + wr : in std_logic; + reset : in std_logic; + a0 : in std_logic; + + -- SPI wires + sck : out std_logic; + sdcs : out std_logic; + sdo : out std_logic; -- reg + sdi : in std_logic; + + -- data + din : in std_logic_vector (7 downto 0); + dout : out std_logic_vector (7 downto 0) -- reg + ); +end component; + + begin --CPM -- Disable ROM if out 38. Re-enable when (asynchronous) reset pressed @@ -234,6 +263,24 @@ port map( clk => sdClock -- twice the spi clk ); +usb : ch376s +port map ( + sdcs => usbCS, + sdo => usbMOSI, + sdi => usbMISO, + sck => usbSCLK, + + wr => not (n_ch376sCS or n_ioWR), + rd => not (n_ch376sCS or n_ioRD), + + dout => ch376sDataOut, + din => cpuDataOut, + + a0 => cpuAddress (0), + reset => not N_RESET, + clk => sdClock +); + -- ____________________________________________________________________________________ -- MEMORY READ/WRITE LOGIC GOES HERE @@ -250,6 +297,7 @@ n_memRD <= n_RD or n_MREQ; n_basRomCS <= '0' when cpuAddress(15 downto 13) = "000" and n_RomActive = '0' else '1'; --8K at bottom of memory n_interface1CS <= '0' when cpuAddress(7 downto 1) = "1000000" and (n_ioWR='0' or n_ioRD = '0') else '1'; -- 2 Bytes $80-$81 n_interface2CS <= '0' when cpuAddress(7 downto 1) = "1000001" and (n_ioWR='0' or n_ioRD = '0') else '1'; -- 2 Bytes $82-$83 +n_ch376sCS <= '0' when cpuAddress(7 downto 1) = "0010000" and (n_ioWR='0' or n_ioRD = '0') else '1'; -- 2 Bytes $20-$21 n_sdCardCS <= '0' when cpuAddress(7 downto 3) = "10001" and (n_ioWR='0' or n_ioRD = '0') else '1'; -- 8 Bytes $88-$8F n_internalRam1CS <= not n_basRomCS; -- Full Internal RAM - 64 K @@ -260,6 +308,7 @@ n_internalRam1CS <= not n_basRomCS; -- Full Internal RAM - 64 K cpuDataIn <= interface1DataOut when n_interface1CS = '0' else interface2DataOut when n_interface2CS = '0' else +ch376sDataOut when n_ch376sCS = '0' else sdCardDataOut when n_sdCardCS = '0' else basRomData when n_basRomCS = '0' else internalRam1DataOut when n_internalRam1CS= '0' else @@ -272,6 +321,8 @@ x"FF"; -- SUB-CIRCUIT CLOCK SIGNALS serialClock <= serialClkCount(15); +--sdClock <= clk; + process (clk) begin if rising_edge(clk) then @@ -281,19 +332,20 @@ begin else cpuClkCount <= (others=>'0'); end if; - if cpuClkCount < 2 then -- 2 when 10MHz, 2 when 12.5MHz, 2 when 16.6MHz, 1 when 25MHz + + if cpuClkCount < 4 then -- 2 when 10MHz, 2 when 12.5MHz, 2 when 16.6MHz, 1 when 25MHz cpuClock <= '0'; else cpuClock <= '1'; end if; - if sdClkCount < 49 then -- 1MHz + if sdClkCount < 1 then -- 25MHz sdClkCount <= sdClkCount + 1; else sdClkCount <= (others=>'0'); end if; - if sdClkCount < 25 then + if sdClkCount < 1 then -- 12,5Mhz sdClock <= '0'; else sdClock <= '1'; diff --git a/MultiComp.qsf b/MultiComp.qsf index eea8a64..cd6691f 100644 --- a/MultiComp.qsf +++ b/MultiComp.qsf @@ -70,7 +70,7 @@ set_global_assignment -name SEED 1 #set_global_assignment -name VERILOG_MACRO "USE_DDRAM=1" #do not enable DEBUG_NOHDMI in release! -#set_global_assignment -name VERILOG_MACRO "DEBUG_NOHDMI=1" +# set_global_assignment -name VERILOG_MACRO "DEBUG_NOHDMI=1" source sys/sys.tcl set_global_assignment -name FAMILY "Cyclone V" @@ -243,8 +243,6 @@ set_location_assignment PIN_W20 -to SW[3] set_hps_location_assignment HPSINTERFACEPERIPHERALSPIMASTER_X52_Y72_N111 -to spi set_hps_location_assignment HPSINTERFACEPERIPHERALUART_X52_Y67_N111 -to uart set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:sys/build_id.tcl" -set_global_assignment -name CDF_FILE jtag.cdf -set_global_assignment -name QIP_FILE sys/sys.qip source sys/sys_analog.tcl set_location_assignment PIN_AF25 -to SDIO_DAT[0] set_location_assignment PIN_AF23 -to SDIO_DAT[1] @@ -295,6 +293,10 @@ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED_* set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to BTN_* set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to BTN_* +set_global_assignment -name VERILOG_FILE Components/USB/spi_master_simple.v +set_global_assignment -name VERILOG_FILE Components/USB/ch376s.v +set_global_assignment -name CDF_FILE jtag.cdf +set_global_assignment -name QIP_FILE sys/sys.qip set_global_assignment -name QIP_FILE rtl/pll.qip set_global_assignment -name SYSTEMVERILOG_FILE MultiComp.sv set_global_assignment -name VHDL_FILE Components/M6809/cpu09l.vhd @@ -325,4 +327,5 @@ set_global_assignment -name QIP_FILE ROMS/Z80/Z80_CPM_BASIC_ROM.qip set_global_assignment -name VHDL_FILE MicrocomputerZ80CPM.vhd set_global_assignment -name VHDL_FILE Microcomputer6502Basic.vhd set_global_assignment -name VHDL_FILE Microcomputer6809Basic.vhd +set_global_assignment -name VHDL_FILE MicrocomputerZ80Basic.vhd set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/MultiComp.qws b/MultiComp.qws index 7c92db1..1c47226 100644 Binary files a/MultiComp.qws and b/MultiComp.qws differ diff --git a/MultiComp.sv b/MultiComp.sv index 25e40cf..8ffc5c4 100644 --- a/MultiComp.sv +++ b/MultiComp.sv @@ -159,7 +159,6 @@ module emu ); assign ADC_BUS = 'Z; -assign USER_OUT = '1; assign {SD_SCK, SD_MOSI, SD_CS} = 'Z; assign {SDRAM_DQ, SDRAM_A, SDRAM_BA, SDRAM_CLK, SDRAM_CKE, SDRAM_DQML, SDRAM_DQMH, SDRAM_nWE, SDRAM_nCAS, SDRAM_nRAS, SDRAM_nCS} = 'Z; assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = 0; @@ -176,13 +175,18 @@ assign VIDEO_ARX = 4; assign VIDEO_ARY = 3; assign VGA_SL = 0; assign VGA_F1 = 0; -//assign CE_PIXEL=1; assign AUDIO_S = 0; assign AUDIO_L = 0; assign AUDIO_R = 0; assign AUDIO_MIX = 0; +// enable input on USER_IO[3] for ch376s MISO +//assign USER_OUT[0] = 1'b0; +//assign USER_OUT[1] = 1'b0; +//assign USER_OUT[3] = 1'b1; +//assign USER_OUT[6] = 1'b0; + `include "build_id.v" localparam CONF_STR = { "MultiComp;;", @@ -351,27 +355,51 @@ begin UART_TXD <= _txd[cpu_type]; end +reg [3:0] test; + +always @(posedge clk_sys) begin + if (reset) begin + test <= 4'd0; + end + test <= test + 4'd1; + + USER_OUT[0] <= test[0]; + USER_OUT[1] <= test[0]; + + USER_OUT[2] <= test[0]; + USER_OUT[3] <= test[1]; + USER_OUT[4] <= test[2]; + USER_OUT[5] <= test[3]; + + USER_OUT[6] <= test[3]; +end + MicrocomputerZ80CPM MicrocomputerZ80CPM ( - .N_RESET(~reset & cpu_type == cpuZ80CPM), - .clk(cpu_type == cpuZ80CPM ? clk_sys : 0), - .R(_r[0][1:0]), - .G(_g[0][1:0]), - .B(_b[0][1:0]), - .HS(_hs[0]), - .VS(_vs[0]), - .hBlank(_hblank[0]), - .vBlank(_vblank[0]), - .cepix(_CE_PIXEL[0]), - .ps2Clk(PS2_CLK), - .ps2Data(PS2_DAT), - .sdCS(_SD_CS[0]), - .sdMOSI(_SD_MOSI[0]), - .sdMISO(sdmiso), - .sdSCLK(_SD_SCK[0]), - .driveLED(_driveLED[0]), - .rxd1 (UART_RXD), - .txd1 (_txd[0]) + .N_RESET (~reset & cpu_type == cpuZ80CPM), + .clk (cpu_type == cpuZ80CPM ? clk_sys : 0), + .R (_r[0][1:0]), + .G (_g[0][1:0]), + .B (_b[0][1:0]), + .HS (_hs[0]), + .VS (_vs[0]), + .hBlank (_hblank[0]), + .vBlank (_vblank[0]), + .cepix (_CE_PIXEL[0]), + .ps2Clk (PS2_CLK), + .ps2Data (PS2_DAT), + .sdCS (_SD_CS[0]), + .sdMOSI (_SD_MOSI[0]), + .sdMISO (sdmiso), + .sdSCLK (_SD_SCK[0]), + .driveLED (_driveLED[0]), + .rxd1 (UART_RXD), + .txd1 (_txd[0]), + // CH376s via USERIO + //.usbSCLK (USER_OUT[2]), + //.usbMISO (USER_IN[3]), + //.usbMOSI (USER_OUT[4]), + //.usbCS (USER_OUT[5]) ); MicrocomputerZ80Basic MicrocomputerZ80Basic diff --git a/Z80 CPM and bootloader (basmon)/TASM.EXE b/Z80 CPM and bootloader (basmon)/TASM.EXE new file mode 100644 index 0000000..4e1f77c Binary files /dev/null and b/Z80 CPM and bootloader (basmon)/TASM.EXE differ diff --git a/Z80 CPM and bootloader (basmon)/TASM.TXT b/Z80 CPM and bootloader (basmon)/TASM.TXT new file mode 100644 index 0000000..3a76dd4 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/TASM.TXT @@ -0,0 +1,1709 @@ + + + + + + + TASM USER'S MANUAL + + + TASM - A Table Driven Cross Assembler for the MSDOS* Environment + + + + + + Thomas N. Anderson + Speech Technology Incorporated + 837 Front Street South, Issaquah, WA 98027 + + + March, 1992 + Version 2.9 + +[Speech Technology Incorporated manufactures electronic devices to aid the +visually impaired employing digital speech synthesis technology.] + + +TASM software is Copyright (C) 1985-1992 by Speech Technology Incorporated. +All rights reserved. + +This document is Copyright (C) 1985-1992 by Speech Technology Incorporated. +All rights reserved. Permission is granted to copy this document and related +software except for the source code. The source code, distributed to +registered users, may be copied for the sole use of the registered user. + + + + + * MSDOS is a trademark of Microsoft Corporation. + + TASM - Table Driven Assembler Version 2.9 Page 2 + +TABLE OF CONTENTS + +SECTION PAGE +______________________________________________________________________ +INTRODUCTION 4 +INVOCATION 5 + File Names 5 + Option: a - Assembly Control 6 + Option: b - Binary Object Format 7 + Option: c - Contiguous Block Output 7 + Option: d - Define a Macro 7 + Option: e - Expand Source 7 + Option: f - Fill Memory 7 + Option: g - Object File Format 7 + Option: h - Hex Object Code Table 7 + Option: i - Ignore Case in Labels 8 + Option: l - Label Table 8 + Option: m - MOS Technology Object Format 8 + Option: o - Set Number of Bytes per Object Record 8 + Option: p - Page Listing File 8 + Option: q - Disable Listing File 8 + Option: r - Set Read Buffer Size 8 + Option: s - Enable Symbol File Generation 9 + Option: t - Table Name 9 + Option: x - Enable Extended Instruction Set 10 + Option: y - Enable Assembly Timing 10 +ENVIRONMENT VARIABLES 11 + TASMTABS 11 + TASMOPTS 11 +EXIT CODES 11 +SOURCE FILE FORMAT 12 +EXPRESSIONS 13 + Labels 13 + Numeric Constants 13 + Character Constants 14 + String Constants 14 + Location Counter Symbol 14 + Operators 15 +ASSEMBLER DIRECTIVES 17 + ADDINSTR 17 + AVSYM 17 + BLOCK 17 + BSEG/CSEG/DSEG/NSEG/XSEG 17 + BYTE 17 + CHK 18 + CODES/NOCODES 18 + DB 18 + DW 18 + DEFINE 18 + DEFCONT 19 + EJECT 19 + ELSE 20 + END 20 + ENDIF 20 + EQU 20 + EXPORT 21 + TASM - Table Driven Assembler Version 2.9 Page 3 + + IFDEF 21 + IFNDEF 21 + IF 22 + INCLUDE 22 + LIST/NOLIST 22 + LOCALLABELCHAR 22 + LSFIRST/MSFIRST 22 + ORG 22 + PAGE/NOPAGE 23 + SET 23 + SYM/AVSYM 23 + TEXT 24 + TITLE 25 + WORD 26 +OBJECT FILE FORMATS 27 + Intel Hex Object Format 27 + MOS Technology Hex Object Format 28 + Motorola Hex Object Format 28 + Binary Object Format 28 +LISTING FILE FORMAT 30 +PROM PROGRAMMING 31 +ERROR MESSAGES 32 +BUGS AND LIMITATIONS 35 + +APPENDIX A - ORDERING INFORMATION 36 + TASM - Table Driven Assembler Version 2.9 Page 4 + +INTRODUCTION + + +TASM is a table driven cross assembler for the MS-DOS environment. Assembly +source code, written in the appropriate dialect (generally very close to the +manufacturers assembly language), can be assembled with TASM, and the +resulting object code transferred to the target microprocessor system via +PROM or other mechanisms. + +The current microprocessor families supported by TASM are: + + 6502 8048 + 6800/6801/68HC11 8051 + 6805 8080/8085 + TMS32010/TMS320C25 Z80 + TMS7000 + +The user so inclined may build tables for other microprocessors. The +descriptions of the various existing tables and instructions on building new +tables are not in this document but can be found in the TASMTABS.DOC file on +the TASM distribution disk. + +TASM characteristics include: + + 1. Powerful expression parsing (17 operators). + 2. Supports a subset of the 'C' preprocessor commands. + 3. Macro capability (through use of DEFINE directive). + 4. Multiple statements per line. + 5. Four object file formats: + Intel hex, MOS Technology hex, Motorola hex, binary. + 6. Absolute code generation only. + 7. Source code available (in C). + 8. Uniform syntax across versions for different target machines. + 9. Features in support of PROM programming (preset memory, + contiguous block). + 10. Supports extended instructions for many of the supported + microprocessor families. + 11. Tables read at run time; single TASM executable for all table versions. + 12. Symbol table export for inclusion in subsequent assemblies. + 13. Symbol table export file compatible with the Avocet 8051 + simulator (.SYM format). + +SHAREWARE + +TASM is distributed as shareware. TASM is not public domain. The TASM +distribution files may be freely copied (excluding the source code files) and +freely used for the purpose of evaluating the suitability of TASM for a given +purpose. Use of TASM beyond a reasonable evaluation period requires +registration. Prolonged use without registration is unethical. + + TASM - Table Driven Assembler Version 2.9 Page 5 + +INVOCATION + +TASM can be invoked as follows (optional fields shown in brackets, symbolic +fields in italics): + + tasm -pn [-option_flag ...] src_file + [obj_file [lst_file [exp_file [sym_file]]]] + + Where option_flag can be one or more of the following: + + -table Specify version (table = table designation) + -ttable Table (alternate form of above) + -aamask Assembly control (optional error checking) + -b Produce object in binary (.COM) format + -c Object file written as a contiguous block + -dmacro Define a macro (or just a macro label) + -e Show source lines after macro expansion + -ffillbyte Fill entire memory space with fillbyte (hex) + -gobjtype Object file (0=Intel, 1=MOS Tech, 2=Motorola, 3=binary) + -h Produce hex table of the assembled code (in list file) + -i Ignore case for labels + -l[al] Produce a label table in the listing + -m Produce object in MOS Technology format + -oobytes Bytes per object record (for hex obj formats) + -p[lines] Page the listing file (lines per page. default=60) + -q Quiet, disable the listing file + -rkb Set read buffer size in Kbytes (default 2 Kbytes) + -s Write a symbol table file + -x[xmask] Enable extended instruction set (if any) + -y Time the assembly + +The file parameters are defined as follows: + +src_file Source file name +obj_file Object code file name +lst_file Listing file name +exp_file Symbol export file (only if the EXPORT directive is used). +sym_file Symbol table file + (only if the "-s" option or the SYM/AVSYM directives are used). + +The source file must be specified. If not, some usage information is +displayed. Default file names for all the other files are generated if they +are not explicitly provided. The filename is formed by taking the source +filename and changing the extension to one of the following: + +Extension File type +________________________________________________________ +.OBJ Object file +.LST Listing file +.EXP Symbol export file +.SYM Symbol table file + +TASM has no built-in instruction set tables. Instruction set definition +files are read at run time. TASM determines which table to use based on the +'-table' field shown above. For example, to assemble the code in a file +called source.asm, one would enter: + TASM - Table Driven Assembler Version 2.9 Page 6 + + tasm -48 source.asm for an 8048 assembly + tasm -65 source.asm for a 6502 assembly + tasm -51 source.asm for an 8051 assembly. + tasm -85 source.asm for an 8085 assembly. + tasm -80 source.asm for a Z80 assembly. + tasm -05 source.asm for a 6805 assembly. + tasm -68 source.asm for a 6800/6801/68HC11 assembly. + tasm -70 source.asm for a TMS7000 assembly. + tasm -3210 source.asm for a TMS32010 assembly. + tasm -3225 source.asm for a TMS320C25 assembly. + +The file name that the tables are read from is formed by taking the digits +specified after the '-' and appending it to 'TASM' then appending the '.TAB' +extension. Thus, the '-48' flag would cause the tables to be read from the +file 'TASM48.TAB'. + +It is possible to designate tables by non numeric part numbers if the '-t' +flag is used. For example, if a user built a table called TASMF8.TAB then +TASM could be invoked as follows: + + tasm -tf8 source.asm + +Each option flag must be preceded by a dash. Options need not precede the +file names, however. The various options are described below: + +a - Assembly Control. TASM can provide additional error checking by +specifying the '-a' option at the time of execution. If the '-a' is provided +without a digit following, then all the available error checking is done. If +a digit follows, then it is used as a mask to determine the error checks to +be made. The bits of the mask are defined as follows: + +Bit Option Flag Description +___________________________________________________________________________ +0 -a1 Check for apparent illegal use of indirection +1 -a2 Check for unused data in the arguments +2 -a4 Check for duplicate labels +3 -a8 Check for non-unary operators at start of expression. +4 -a10 Check for 8051 AJMP/ACALL branches off current 2K page. + +Combinations of the above bits can also be used. For example, '-a5' would +enable the checking for illegal indirection and duplicate labels. + +Illegal indirection applies to micros that use parenthesis around an argument +to indicate indirection. Since it is always legal to put an extra pair of +parenthesis around any expression (as far as the expression parser is +concerned), the user may think that he/she is indicating indirection for an +instruction that has no indirection and TASM would not complain. Enabling +this checking will result in an error message (warning) whenever an outer +pair of parenthesis is used and the instruction set definition table does not +explicitly indicate that to be a valid form of addressing. + +Unused data in arguments applies to cases where a single byte of data is +needed from an argument, but the argument contains more than one byte of +data. If a full sixteen bit address is used in a 'Load Immediate' type +instruction that needs only a single byte, for example, an error message +would be generated. Here is an example (6502 code): + TASM - Table Driven Assembler Version 2.9 Page 7 + +0001 1234 .org $1234 +tasm: Unused data in MS byte of argument. Line 0002 in test.asm +0002 1234 A9 34 start lda #start + +To make the above checks occur whenever you do an assembly, add a line like +this to your AUTOEXEC.BAT file: + + SET TASMOPTS=-a + +b - Binary Object Format. This option causes the object file to be written +in binary - one byte for each byte of code/data. Note that no address +information is included in the object file in this format. The contiguous +block (-c) output mode is forced when this option is invoked. This flag is +equivalent to '-g3'. + +c - Contiguous Block Output. If this option is specified, then all bytes in +the range from the lowest used byte to the highest will be defined in the +object file. Normally, with the default Intel Hex object format enabled, if +the Program Counter (PC) jumps forward because of an .ORG directive, the +bytes skipped over will not have any value assigned them in the object file. +With this option enabled, no output to the object file occurs until the end +of the assembly at which time the whole block is written. This is useful +when using TASM to generate code that will be put into a PROM so that all +locations will have a known value. This option is often used in conjunction +with the -f option to ensure all unused bytes will have a known value. + +d - Define a Macro. Macros are defined on the command line generally to +control the assembly of various IFDEF's that are in the source file. This +is a convenient way to generate various versions of object code from a single +source file. + +e - Expand Source. Normally TASM shows lines in the listing file just as they +are in the source file. If macros are in use (via the DEFINE directive) it +is sometimes desirable to see the source lines after expansion. Use the '-e' +flag to accomplish this. + +f - Fill Memory. This option causes the memory image that TASM +maintains to be initialized to the value specified by the two hex characters +immediately following the 'f'. TASM maintains a memory image that is a full +64K bytes in size (even if the target processor cannot utilize that memory +space). Invocation of this option introduces a delay at start up of up to 2 +seconds (time required to initialize all 64K bytes). + +g - Object File Format. TASM can generate object code in four different +formats as indicated below: + + -g0 for Intel hex (default) + -g1 for MOS Technology hex (same as -m) + -g2 for Motorola hex + -g3 for binary (same as -b) + +The '-m' and '-b' flags may also be used, as indicated above. + +See the section on OBJECT FILE FORMATS for descriptions of each of the above. + + + TASM - Table Driven Assembler Version 2.9 Page 8 + +h - Hex Object Code Table. This option causes a hex table of the produced +object code to appear in the listing file. Each line of the table shows +sixteen bytes of code. + + +i - Ignore Case in Labels. TASM is normally case sensitive when dealing with +labels. For those that prefer case insensitivity, the '-i' command line +option can be employed. + +l - Label Table. This option causes a label table to appear in the listing +file. Each label is shown with its corresponding value. Macro labels (as +established via the DEFINE directives) do not appear. + +Two optional suffixes may follow the '-l' option: + + Suffix Description + _______________________________________________ + l Use long form listing + a Show all labels (including local labels) + +The suffix should immediately follow the '-l'. Here are some examples: + + -l to show non-local labels in the short form + -la to show all labels in the short form + -ll to show non-local labels in the long form + -lal to show all labels in the long form + + +m - MOS Technology Object Format. This option causes the object file to be +written in MOS Technology hex format rather than the default Intel hex +format. See section on OBJECT FILE FORMATS for a description of the format. + +o - Set Number of Bytes per Object Record. When generating object code in +either the MOS Technology format or the Intel hex format, a default of 24 +(decimal) bytes of object are defined on each record. This can be altered by +invoking the '-o' option immediately followed by two hex digits defining +the number of bytes per record desired. For example, if 32 bytes per record +are desired, one might invoke TASM as: + + TASM -48 -o20 source.asm + +p - Page Listing File. This option causes the listing file to have top of +page headers and form feeds inserted at appropriate intervals (every sixty +lines of output). To override the default of sixty lines per page, indicate +the desired number of lines per page as a decimal number immediatly following +the '-p'. Here is an example: + + TASM -48 -p56 source.asm + +q - Disable Listing File. This option causes all output to the listing file +to be suppressed, unless a .LIST directive is encountered in the source file +(see LIST/NOLIST directives). + +r - Set Read Buffer Size. This option overrides the default read buffer size +of 2 Kbytes. The first hexadecimal digit immediately after the 'r' is taken +as the number of K bytes to allocate for the read buffer (.e.g. '-r8' + TASM - Table Driven Assembler Version 2.9 Page 9 + +indicates an 8K byte buffer, '-rf' indicates a 15K byte buffer). Note that +that read buffers are taken from the same memory pool as labels and macro +storage, and that additional read buffers are needed if "includes" are used. +Thus, using 8K byte buffers may be suitable for most assemblies, but programs +with large numbers of symbols may not allow such a value. Also, reducing the +buffer size to 1 Kbyte can increase the memory pool available for label +storage, if such is needed. + +s - Enable Symbol File Generation. If this flag is set, a symbol file is +generated at the end of the assembly. The format of the file is one line per +label, each label starts in the first column and is followed by white space +and then four hexadecimal digits representing the value of the label. The +following illustrates the format: + + label1 FFFE + label2 FFFF + label3 1000 + +The symbol file name can be provided as the fifth file name on the the +command line, or the name will be generated from the source file name with a +'.SYM' extension. The symbol table file can also be generated by invoking +the SYM directive. The AVSYM directive also generates the symbol file but in +a different format (see section on ASSEMBLER DIRECTIVES). + +t - Table Name. As an alternative to specifying the instruction set table as +two decimal digits, the table indication may be preceeded by the '-t' option. +This is useful if the desired table name starts with a non-numeric. Thus, a +table for an F8 might be selected as: + + TASM -tf8 source.asm + +TASM would expect to read the instruction set definition tables from a file +named TASMF8.TAB. + TASM - Table Driven Assembler Version 2.9 Page 10 + + +x - Enable Extended Instruction Set. If a processor family has instructions +that are valid for only certain members, this option can be used to enable +those beyond the basic standard instruction set. A hex digit may follow the +'x' to indicate a mask value used in selecting the appropriate instruction +set. Bit 0 of the mask selects the basic instruction set, thus a '-x1' would +have no effect. A '-x3' would enable the basic set plus whatever +instructions have bit 1 set in their class mask. A '-x' without a digit +following is equivalent to a '-xf' which sets all four of the mask bits. The +following table indicates the current extended instruction sets available in +the TASM tables: + +Base Base Extension 1 Extension 2 Extension 3 Extension 4 +Table Family (-x3) (-x7) (-x5) (-x9) +_____________________________________________________________________________ +48 8048 8041A 8022 8021 +65 6502 R65C02 R65C00/21 +05 6805 M146805 CMOS HC05C4 +80 Z80 HD64180 +68 6800 6801/6803 68HC11 +51 8051 +85 8080 +3210 TMS32010 +3225 TMS320C25 TMS320C26 +70 TMS7000 + +The above table does not attempt to show the many microprocessor family +members that may apply under a given column. + +See the TASMTABS.DOC on-line document for details on each specific table. + +y - Enable Assembly Timing. If this option is enabled TASM will generate a +statement of elapsed time and assembled lines per second at the end of the +assembly. + + TASM - Table Driven Assembler Version 2.9 Page 11 + +ENVIRONMENT VARIABLES + +The TASM environment can be customized by using the enviroment variables +listed below: + +TASMTABS. This variable specifies the path to be searched for TASM +instruction set definition tables. If it is not defined then the table(s) +must exist in the current working directory. If it was desired to put the +instruction set definition tables in a directory called 'TASM' on a drive +called 'C:', the following statement would be appropriate in the AUTOEXEC.BAT +file: + + set TASMTABS=C:\TASM + +TASMOPTS. This variable specifies TASM command line options that are to be +invoked every time TASM is executed. For example, if TASM is being used for +8048 assemblies with binary object file output desired, the following +statement would be appropriate in the AUTOEXEC.BAT file: + + set TASMOPTS=-48 -b + + +EXIT CODES + +When TASM terminates, it will return to DOS the following exit codes: + +Exit Code Meaning +___________________________________________________________ +0 Normal completion, no assembly errors +1 Normal completion, with assembly errors +2 Abnormal completion, insufficient memory +3 Abnormal completion, file access error +4 Abnormal completion, general error + + +Exit codes 2 and above will also be accompanied by messages to the console +concerning the error. + + TASM - Table Driven Assembler Version 2.9 Page 12 + +SOURCE FILE FORMAT + +Statements in the source file must conform to a format as follows (except +for assembler directive statements which are described in a subsequent +section): + + label operation operand comment + +All of the fields are optional, under appropriate circumstances. An +arbitrary amount of white space (space and tabs) can separate each field (as +long as the maximum line length of 255 characters is not exceeded). Each of +the fields are described below: + +Label Field. If the first character of the line is alphabetic, it is assumed +to be the start of a label. Subsequent characters are accepted as part of +that label until a space, tab, or ':' is encountered. The assembler assigns +a value to the label corresponding to the current location counter. Labels +can be a maximum of 32 characters long. Labels can contain upper and lower +case letters, digits, underscores, and periods (the first character must be +alphabetic). Labels are case sensitive - the label 'START' is a different +label from 'start' - unless the '-i' (ignore case) option is enabled. + +Operation Field. The operation field contains an instruction mnemonic which +specifies the action to be carried out by the target processor when this +instruction is executed. The interpretation of each mnemonic is dependent on +the target microprocessor (as indicated by the selected TASM table). The +operation field may begin in any column except the first. The operation +field is case insensitive. + +Operand Field. The operand field specifies the data to be operated on by the +instruction. It may include expressions and/or special symbols describing +the addressing mode to be used. The actual format and interpretation is +dependent on the target processor. For a description of the format for +currently supported processors, see the TASMTABS.DOC file on the TASM +distribution disk. + +Comment Field. The comment field always begins with a semicolon. The rest +of the line from the semicolon to the end of the line is ignored by TASM, but +passed on to the listing file for annotation purposes. The comment field +must be the last field on a line, but it may be the only field, starting in +column one, if desired. + +Multiple Statement Lines. If the backslash character is encountered on a +source line, it is treated as a newline. The remainder of the line +following the backslash will be processed as an independent line of source +code. This allows one to put multiple statements on a line. This facility +is not so useful of itself, but when coupled with the capability of the +DEFINE directive, powerful multiple statement macros can be +constructed (see section on ASSEMBLER DIRECTIVES). Note that when using +the statement separator, the character immediately following it should +be considered the first character of a new line, and thus must either be a +start of a label or white space (not an instruction). As the examples show, +a space is put between the backslash and the start of the next instruction. + + +Some examples of valid source statements follow (6502 mnemonics shown): + TASM - Table Driven Assembler Version 2.9 Page 13 + + +label1 lda byte1 ;get the first byte + dec byte1 + jne label1 + +; +label2 + sta byte2,X + ; a multiple statement line follows + lda byte1\ sta byte1+4\ lda byte2\ sta byte2+4 + + + +EXPRESSIONS + +Expressions are made up of various syntactic elements (tokens) combined +according to a set of syntactical rules. The tokens are summarized as +follows: + + 1. Labels + 2. Constants + 3. Location Counter Symbol + 4. Operators + 5. Parenthesis + +Labels. Labels are strings of characters that have a numeric value associated +with them, generally representing an address. Labels can contain upper and +lower case letters, digits, underscores, and periods. The first character +must be a letter or the local label prefix (default '_'). The value of a +label is limited to 32 bit precision. Labels can contain up to 32 +characters, all of which are significant (none are ignored when looking at a +label's value, as in some assemblers). Case is significant unless the '-i' +command line option is invoked. + +Local labels must only be unique within the scope of the current module. +Modules are defined with the MODULE directive. Here is an example: + + .MODULE xxx + lda regx + jne _skip + dec + _skip rts + + .MODULE yyy + lda regy + jne _skip + dec + _skip rts + + +In the above example, the _skip label is reused without harm. As a default, +local labels are not shown in the label table linting (resulting from the '- +l' command line option). See also sections on MODULE and LOCALLABELCHAR +directives. + +Numeric Constants. Numeric constants must always begin with a decimal digit + TASM - Table Driven Assembler Version 2.9 Page 14 + +(thus hexadecimal constants that start with a letter must be prefixed by a +'0' unless the '$' prefix is used). The radix is determined by a letter +immediately following the digit string according to the following table: + + Radix Suffix Prefix + ______________________________________________________ + 2 B or b % + 8 O or o @ + 10 D or d (or nothing) + 16 H or h $ + +Decimal is the default radix, so decimal constants need no suffix or prefix. + +The following representations are equivalent: + + 1234H or $1234 + 100d or 100 + 177400O or @177400 + 01011000b or %01011000 + +The prefixes are provided for compatibility with some other source code +formats but introduce a problem of ambiguity. Both '%' and '$' have +alternate uses ('%' for modulo, '$' for location counter symbol). The +ambiguity is resolved by examining the context. The '%' character is +interpreted as the modulo operator only if it is in a postion suitable for a +binary operator. Similarly, if the first character following a '$' is a valid +hexadecimal digit, it is assumed to be a radix specifier and not the location +counter. + +Character Constants. Character constants are single characters surrounded by +single quotes (following quote is optional). The ASCII value of the +character in the quotes is returned. No escape provision exists to represent +non-printable characters within the quotes, but this is not necessary since +these can be just as easily represented as numeric constants (or using the +TEXT directive which does allow escapes). + +String Constants. String constants are one or more characters surrounded by +double quotes. Note that string constants are not allowed in expressions. +They are only allowable following the TITLE, BYTE and TEXT assembler +directives. The quoted strings may also contain escape sequences to put in +unprintable values. The following escape sequences are supported: + + Escape + Sequence Description + ___________________________________________________________ + \n Line Feed + \r Carriage return + \b Backspace + \t Tab + \f Formfeed + \\ Backslash + \" Quote + \000 Octal value of character + +Location Counter Symbol. The current value of the location counter (PC) +can be used in expressions by placing a '$' in the desired place. The + TASM - Table Driven Assembler Version 2.9 Page 15 + +Location Counter Symbol is allowable anywhere a numeric constant is. (Note +that if the '$' is followed by a decimal digit then it is taken to be the +hexadecimal radix indicator instead of the Location Counter symbol, as +mentioned above). The '*' may also be used to represent the location +counter, but is less preferred because of its ambiguity with the +multiplicative operator. + +Operators. Expressions can optionally contain operators to perform some +alterations or calculations on particular values. The operators are +summarized as follows: + + Operator Type Description + __________________________________________ + + Additive addition + - subtraction + + * Multiplicative multiplication + / division + % modulo + << logical shift left + >> logical shift right + + ~ Unary bit inversion (one's complement) + - unary negation + + = Relational equal + == equal + != not equal + < less than + > greater than + <= less than or equal + >= greater than or equal + + & Binary binary 'and' + | binary 'or' + ^ binary 'exclusive or' + +The syntax is much the same as in 'C' with the following notes: + +1. No operator precedence is in effect. Evaluation is from left to right +unless grouped by parenthesis (see example below). + +2. All evaluations are done with 32 bit signed precision. + +3. Both '=' and '==' are allowable equality checkers. This is allowed since +the syntax does not provide assignment capability (as '=' would normally +imply). + +The relational operators return a value of 1 if the relation is true and 0 if +it is false. Thirtytwo bit signed arithmetic is used. + +It is always a good idea to explicitly indicate the desired order of +evaluation with parenthesis, especially to maintain portability since +TASM does not evaluate expressions in the same manner as many other +assemblers. To understand how it does arrive at the values for +expressions, consider the following example: + TASM - Table Driven Assembler Version 2.9 Page 16 + + 1 + 2*3 + 4 + +TASM would evaluate this as: + + (((1 + 2) * 3) + 4) = 13 + +Typical rules of precedence would cause the (2*3) to be evaluated first, such +as: + + 1 + (2*3) + 4 = 11 + +To make sure you get the desired order of evaluation, use parenthesis +liberally. + +Note that TASM versions earlier than 2.7.8 employed a somewhat different +method of evaluating expressions (also without precedence) that essentially +resulted in a right to left evaluation. + +Here are some examples of valid expressions: + + (0f800H + tab) + (label_2 >> 8) + (label_3 << 8) & $f000 + $ + 4 + 010010000100100b + 'a' + (base + ((label_4 >> 5) & (mask << 2)) + TASM - Table Driven Assembler Version 2.9 Page 17 + +ASSEMBLER DIRECTIVES + +Most of the assembler directives have a format similar to the machine +instruction format. However, instead of specifying operations for the +processor to carry out, the directives cause the assembler to perform some +function related to the assembly process. TASM has two types of assembler +directives - those that mimic the 'C' preprocessor functions, and those that +resemble the more traditional assembler directive functions. Each of these +will be discussed. + +The 'C' preprocessor style directives are invoked with a '#' as the first +character of the line followed by the appropriate directive (just as in 'C'). +Thus, these directives cannot have a label preceding them (on the same line). +Note that in the examples directives are shown in upper case, however, either +upper or lower case is acceptable. + +ADDINSTR. This directive can be used to define additional instructions for +TASM to use in this assembly. The format is: + +[label] .ADDINSTR inst args opcode nbytes modop class shift binor + +The fields are separated by white space just as they would appear in an +instruction definition file. See the TASMTABS.DOC file on the TASM +distribution disk for more detail. + +AVSYM. See SYM/AVSYM. + +BLOCK. This directive causes the Instruction Pointer to advance the specified +number of bytes without assigning values to the skipped over locations. The +format is: + + [label] .BLOCK expr + +Some valid examples are: + + word1 .BLOCK 2 + byte1 .block 1 + buffer .block 80 + +BSEG/CSEG/DSEG/NSEG/XSEG. These directives can be invoked to indicate the +appropriate address space for symbols and labels defined in the subsequent +code. The invocation of these directives in no way affects the code +generated, only provides more information in the symbol table file if the +AVSYM directive is employed. Segment control directives such as these are +generally supported by assemblers that generate relocatable object code. +TASM does not generate relocatable object code and does not support a link +phase, so these directives have no direct effect on the resulting object +code. The segments are defined as follows: + + Directive Segment Description + _________________________________________________________ + BSEG Bit address + CSEG Code address + DSEG Data address (internal RAM) + NSEG Number or constant (EQU) + XSEG External data address (external RAM) + TASM - Table Driven Assembler Version 2.9 Page 18 + +BYTE. This directive allows a value assignment to the byte pointed to by the +current Instruction Pointer. The format is: + + [label] .BYTE expr [, expr ...] + +Onlythe lower eight bits of expr are used. Multiple bytes may be assigned by +separating them with commas or (for printable strings) enclosed in double +quotes. Here are some examples: + + + label1 .BYTE 10010110B + .byte 'a' + .byte 0 + .byte 100010110b,'a',0 + .byte "Hello", 10, 13, "World" + + +CHK. This directive causes a checksum to be computed and deposited at the +current location. The starting point of the checksum calculation is +indicated as an argument. Here is the format: + + .CHK starting_addr + +Here is an example: + + + start: NOP + LDA #1 + .CHK start + + +The checksum is calculated as the simple arithmetic sum of all bytes starting +at the start_add up to but not including the address of the CHK directive. +The least significant byte is all that is used. + +CODES/NOCODES. These directives can be used to alternately turn on or off the +generation of formatted listing output with line numbers, opcodes, data, etc. +With NOCODES in effect, the source lines are sent to the listing file +untouched. This is useful around blocks of comments that need a full 80 +columns of width for clarity. + +DB. This is alternate form of the BYTE directive. + +DW. This is alternate form of the WORD directive. + +DEFINE. The DEFINE directive is one of the most powerful of the directives +and allows string substitution with optional arguments (macros). The format +is as follows: + + #DEFINE macro_label[(arg_list)] [macro_definition] + + macro_label := string to be expanded when found in the source file. + + arg_list := optional argument list for variable substitution + + macro_def := string to replace the occurrences of macro_label in + TASM - Table Driven Assembler Version 2.9 Page 19 + + the source file. + +The simplest form of the DEFINE directive might look like this: + + #DEFINE MLABEL + +Notice that no substitutionary string is specified. The purpose of a +statement like this would typically be to define a label for the purpose of +controlling some subsequent conditional assembly (IFDEF or IFNDEF). + +A more complicated example, performing simple substitution, might look like +this: + + #DEFINE VAR1_LO (VAR1 & 255) + +This statement would cause all occurrences of the string 'VAR1_LO' in the +source to be substituted with '(VAR1 & 255)'. + +As a more complicated example, using the argument expansion +capability, consider this: + + #DEFINE ADD(xx,yy) clc\ lda xx\ adc yy\ sta xx + +If the source file then contained a line like this: + + ADD(VARX,VARY) + +It would be expanded to: + + clc\ lda VARX\ adc VARY\ sta VARX + +The above example shows the use of the backslash ('\') character as a +multiple instruction statement delimiter. This approach allows the +definition of fairly powerful, multiple statement macros. The example +shown generates 6502 instructions to add one memory location to another. + +Some rules associated with the argument list: + +1. Use a maximum of 10 arguments. + +2. Each argument should be a maximum of 15 characters. + +Note that macros can be defined on the TASM command line, also, with the '-d' +option flag. + +DEFCONT. This directive can be used to add to the last macro started with a +DEFINE directive. This provides a convenient way to define long macros +without running off the edge of the page. The ADD macro shown above could be +defined as follows: + + #DEFINE ADD(xx,yy) clc + #DEFCONT \ lda xx + #DEFCONT \ adc yy + #DEFCONT \ sta xx + +EJECT. This directive can be used to force a top of form and the + TASM - Table Driven Assembler Version 2.9 Page 20 + +generation of a page header on the list file. It has no effect if the +paging mode is off (see PAGE/NOPAGE). The format is: + + .EJECT + +ELSE. This directive can optionally be used with IFDEF, IFNDEF and IF to +delineate an alternate block of code to be assembled if the block immediately +following the IFDEF, IFNDEF or IF is not assembled. + +Here are some examples of the use of IFDEF, IFNDEF, IF, ELSE, and ENDIF: + + #IFDEF label1 + lda byte1 + sta byte2 + #ENDIF + + #ifdef label1 + lda byte1 + #else + lda byte2 + #endif + + #ifndef label1 + lda byte2 + #else + lda byte1 + #endif + + #if ($ >= 1000h) + ; generate an invalid statement to cause an error + ; when we go over the 4K boundary. + !!! PROM bounds exceeded. + #endif + +END. This directive should follow all code/data generating statements in +the source file. It forces the last record to be written to the object file. +The format is: + + [label] .END + +ENDIF. This directive must always follow an IFDEF, IFNDEF, or IF directive +and signifies the end of the conditional block. + +EQU. This directive can be used to assign values to labels. The labels can +then be used in expressions in place of the literal constant. The format is: + + label .EQU expr + +Here is an example: + + MASK .EQU 0F0H + ; + lda IN_BYTE + and MASK + sta OUT_BYTE + + TASM - Table Driven Assembler Version 2.9 Page 21 + +An alternate form of 'EQU' is '='. The previous example is equivalent to: + + MASK = 0F0H + +or + + MASK =0FOH + MASK =$FO + +White space must exist after the label, but none is required after the '='. + +EXPORT. This directive can be used to define labels (symbols) that are to be +written to the export symbol file. The symbols are written as equates (using +the .EQU directive) so that the resulting file can be included in a +subsequent assembly. This feature can help overcome some of the deficiencies +of TASM due to its lack of a relocating linker. The format is: + +[label] .EXPORT label + +The following example illustrates the use of the EXPORT directive and the +format of the resulting export file: + +Source file: + + .EXPORT read_byte + .EXPORT write_byte + .EXPORT open_file + +Resulting export file: + +read_byte .EQU $1243 +write_byte .EQU $12AF +open_file .EQU $1301 + + +IFDEF. This directive can be used to optionally assemble a block of code. It +has the following form: + + #IFDEF macro_label + +When invoked, the list of macro labels (established via DEFINE directives) is +searched. If the label is found, the following lines of code are assembled. +If not found, the input file is skipped until an ENDIF or ELSE directive is +found. + +Lines that are skipped over still appear in the listing file, but a '~' will +appear immediately after the current PC and no object code will be generated +(this is applicable to IFDEF, IFNDEF, and IF). + +IFNDEF. This directive is the opposite of the IFDEF directive. The block of +code following is assembled only if the specified macro_label is undefined. +It has the following form: + + #IFNDEF macro_label + +When invoked, the list of macro labels (established via DEFINE directives) is + TASM - Table Driven Assembler Version 2.9 Page 22 + +searched. If the label is not found, the following lines of code are +assembled. If it is found, the input file is skipped until an ENDIF or ELSE +directive is found. + +IF. This directive can be used to optionally assemble a block of code +dependent on the value of a given expression. The format is as follows: + + #IF expr + +If the expression expr evaluates to non-zero, the following block of code is +assembled (until an ENDIF or ELSE is encountered). + +INCLUDE. The INCLUDE directive reads in and assembles the indicated source +file. INCLUDEs can be nested up to six levels. This allows a convenient +means to keep common definitions, declarations, or subroutines in files to be +included as needed. The format is as follows: + + #INCLUDE filename + +The filename must be enclosed in double quotes. Here are some examples: + + #INCLUDE "macros.h" + #include "equates" + #include "subs.asm" + +LIST/NOLIST. These directives can be used to alternately turn the output to +the list file on (LIST) or off (NOLIST). The format is: + + .LIST + .NOLIST + +LOCALLABELCHAR. This directive can be used to override the default "_" as the +label prefix indicating a local label. For example, to change the prefix to +"?" do this: + + .LOCALLABELCHAR "?" + +Be carefull to use only characters that are not operators for expression +evaluation. To do so causes ambiguity for the expression evaluator. Some +safe characters are "?", "{", and "}". + +LSFIRST/MSFIRST. These directives determine the byte order rule to be +employed for the WORD directive. The default (whether correct or not) for +all TASM versions is the least significant byte first (LSFIRST). The +following illustrates its effect: + + 0000 34 12 .word $1234 + 0002 .msfirst + 0002 12 34 .word $1234 + 0004 .lsfirst + 0004 34 12 .word $1234 + + +ORG. This directive provides the means to set the Instruction Pointer (a.k.a. +Program Counter) to the desired value. The format is: + + TASM - Table Driven Assembler Version 2.9 Page 23 + + [label] .ORG expr + +The label is optional. The Instruction pointer is assigned the value of the +expression, expr. For example, to generate code starting at address 1000H, +the following could be done: + + start .ORG 1000H + +The expression (expr) may contain references to the current Instruction +Pointer, thus allowing various manipulations to be done. For example, to +align the Instruction Pointer on the next 256 byte boundary, the following +could be done: + + .ORG (($ + 0FFH) & 0FF00H) + +ORG can also be used to reserve space without assigning values: + + .ORG $+8 + +An alternate form of ORG is '*=' or '$='. Thus the following two examples +are exactly equivalent to the previous example: + + *=*+8 + $=$+8 + +PAGE/NOPAGE. These directives can be used to alternately turn the paging +mode on (PAGE) or off (NOPAGE). If paging is in effect, then every sixty +lines of output will be followed by a Top of Form character and a two line +header containing page number, filename, and the title. The format is: + + .PAGE + .NOPAGE + +The number of lines per page can be set with the '-p' command line option. + + +SET. This directive allows the value of an existing label to be changed. The +format is: + + label .SET expr + +The use of the SET directive should be avoided since changing the value of a +label can sometimes cause phase errors between pass 1 and pass 2 of the +assembly. + +SYM/AVSYM. These directives can be used to cause a symbol table file to be +generated. The format is: + + .SYM ["symbol_filename"] + .AVSYM ["symbol_filename"] + + TASM - Table Driven Assembler Version 2.9 Page 24 + +For example: + + .SYM "symbol.map" + .SYM + .AVSYM "prog.sym" + .AVSYM + +The two directives are similar, but result in a different format of the +symbol table file. The format of the SYM file is one line per symbol, each +symbol starts in the first column and is followed by white space and then +four hexadecimal digits representing the value of the symbol. The following +illustrates the format: + + label1 FFFE + label2 FFFF + label3 1000 + +The AVSYM is provided to generate symbol tables compatible with the Avocet +8051 simulator. The format is similar, but each line is prefixed by an 'AS' +and each symbol value is prefixed by a segment indicator: + + AS start C:1000 + AS read_byte C:1243 + AS write_byte C:1280 + AS low_nib_mask N:000F + AS buffer X:0080 + +The segment prefixes are determined by the most recent segment directive +invoked (see BSEG/CSEG/DSEG/NSEG/XSEG directives). + +TEXT. This directive allows an ASCII string to be used to assign values to a +sequence of locations starting at the current Instruction Pointer. The +format is: + + [label] .TEXT "string" + +The ASCII value of each character in string is taken and assigned to the next +sequential location. Some escape sequences are supported as follows: + + Escape + Sequence Description + ___________________________________________________ + \n Line Feed + \r Carriage return + \b Backspace + \t Tab + \f Formfeed + \\ Backslash + \" Quote + \000 Octal value of character + TASM - Table Driven Assembler Version 2.9 Page 25 + + +Here are some examples: + + message1 .TEXT "Disk I/O error" + message2 .text "Enter file name " + .text "abcdefg\n\r" + .text "I said \"NO\"" + + +TITLE. This directive allows the user to define a title string that appears +at the top of each page of the list file (assuming the PAGE mode is on). The +format is: + + .TITLE "string" + +The string should not exceed 80 characters. Here are some examples: + + .TITLE "Controller version 1.1" + .title "This is the title of the assembly" + .title "" + TASM - Table Driven Assembler Version 2.9 Page 26 + + +WORD. This directive allows a value assignment to the next two bytes pointed +to by the current Instruction Pointer. The format is: + + [label] .WORD expr + +The least significant byte of expr is put at the current Instruction Pointer +with the most significant byte at the next sequential location (unless the +MSFIRST directive has been invoked). Here are some examples: + + data_table .WORD (data_table + 1) + .word $1234 + .Word (('x' - 'a') << 2) + .Word 12, 55, 32 + + TASM - Table Driven Assembler Version 2.9 Page 27 + +OBJECT FILE FORMATS + +TASM supports four object file formats: + + 1. Intel Hex (default). + 2. MOS Technology Hex. + 3. Motorola Hex. + 4. Binary + +Each are described below: + +Intel Hex Object Format. This is the default format. This format is line +oriented and uses only printable ASCII characters except for the carriage +return/line feed at the end of each line. Each line in the file assumes the +following format: + +:NNAAAARRHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHCCTT + +Where: + +All fields marked 'hex' consist of two or four ASCII hexadecimal digits (0-9, +A-F). A maximum of 24 data bytes will be represented on each line (override +the 24 byte default with the '-o' command line option). + +: = Record Start Character +NN = Byte Count (hex) +AAAA = Address of first byte (hex) +RR = Record Type (hex, 00 except for last record which is 01) +HH = Data Bytes (hex) +CC = Check Sum (hex) +TT = Line Terminator (carriage return, line feed) + +The last line of the file will be a record conforming to the above format +with a byte count of zero: + + :00000001FF + +The checksum is defined as: + + sum = byte_count + addr_hi + addr_lo + record_type + (sum of data bytes) + checksum = ((-sum) & ffh) + TASM - Table Driven Assembler Version 2.9 Page 28 + +MOS Technology Hex Object Format. This format is line oriented and uses only +printable ASCII characters except for the carriage return/line feed at the +end of each line. Each line in the file assumes the following format: + +;NNAAAAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHCCCCTT + +All fields marked 'hex' consist of two or four ASCII hexadecimal digits (0-9, +A-F). A maximum of 24 data bytes will be represented on each line (override +the 24 byte default with the '-o' command line option). + +; = Record Start Character +NN = Byte Count (hex) +AAAA = Address of first byte (hex) +HH = Data Bytes (hex) +CCCC = Check Sum (hex) +TT = Line Terminator (carriage return, line feed) + +The last line of the file will be a record with a byte count of zero (';00'). + +The checksum is defined as: + + sum = byte_count + address_hi + address_lo + record_type + + (sum of all data bytes) + checksum = (sum & ffffh) + + +Motorola Hex Object Format. This format is line oriented and uses only +printable ASCII characters except for the carriage return/line feed at the +end of each line. Each line in the file assumes the following format: + +S1NNAAAAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHCCCCTT + +All fields marked 'hex' consist of two or four ASCII hexadecimal digits (0-9, +A-F). A maximum of 24 data bytes will be represented on each line (override +the 24 byte default with the '-o' command line option). + +S1 = Record Start Prefix +NN = Byte Count (hex) (data byte count + 3) +AAAA = Address of first byte (hex) +HH = Data Bytes (hex) +CC = Check Sum (hex) +TT = Line Terminator (carriage return, line feed) + +The last line of the file will be a record with a byte count of zero +('S903AAAACCTT'). The checksum is defined as: + + sum = byte_count + address_hi + address_lo + record_type + + (sum of all data bytes) + checksum = (~sum & ffh) + + +Binary Object Format. This file format has only a binary representation of +each data byte with no address, checksum or format description, whatsoever. +It is often a convenient format to use to pass the data to other programs on +your PC (like a PROM programmer package) but because of the non-printability +and lack of address information, it is not often used to transmit the code to + TASM - Table Driven Assembler Version 2.9 Page 29 + +other systems. + +Note that when this object format is selected (-b option), the -c option is +forced. This is done so that no ambiguity arises as a result of the lack of +address information in the file. Without the -c option, discontinuous blocks +of object code would appear contiguous. + + TASM - Table Driven Assembler Version 2.9 Page 30 + +LISTING FILE FORMAT + +Each line of source code generates one (or more) lines of output in the +listing file. The fields of the output line are as follows: + +1. Current source file line number (4 decimal digits). + +2. An optional '+' appears if this is an 'INCLUDE' file. (One '+' for +each level of INCLUDE invoked). + +3. Current Instruction Pointer (4 hex digits). An optional '~' follows the +Instruction Pointer if the line of source code is not being assembled because +of an IFDEF, IFNDEF, or IF directive. + +4. Resulting code/data generated from this source line (two hex digits per +byte, each byte separated by a space, up to six bytes per line). + +5. The source line exactly as it appears in the source file. + +If paging is enabled (by either the '-p' option flag or the .PAGE +directive) some additional fields will be inserted into the listing file +every 60 lines. These fields are: + + 1. Top of Form (form feed). + 2. Assembler identifier (e.g. "TASM 6502 Assembler"). + 3. Initial source file name. + 4. Page number. + 5. Title. + TASM - Table Driven Assembler Version 2.9 Page 31 + +PROM PROGRAMMING + +A wide variety of PROM programming equipment is available that can use object +code in one or more of the formats TASM supports. Here are some notes +concerning the generation of code to be put in PROMs: + +1. PRESET MEMORY. It is often desirable to have all bytes in the PROM +programmed even if not explicitly assigned a value in the source code (e.g. +the bytes are skipped over with a .ORG statement). This can be accomplished +by using the -c (contiguous block) and the -f (fill) command line option +flags. The -c will ensure that every byte from the lowest byte assigned a +value to the the highest byte assigned a value will be in the object file +with no gaps. The -f flag will assign the specified value to all bytes +before the assembly begins so that when the object file is written, all bytes +not assigned a value in the source code will have a known value. As an +example, the following command line will generate object code in the default +Intel Hex format with all bytes not assigned a value in the source set to EA +(hex, 6502 NOP): + + tasm -65 -c -fEA test.asm + +2. CONTIGUOUS BLOCKS. To ensure that TASM generates object code to cover +the full address range of the target PROM, put a .ORG statement at the end of +the source file set to the last address desired. For example, to generate +code to be put in a 2716 EPROM (2 Kbytes) from hex address $1000 to $17ff, do +something like this in the source file: + + ;start of the file + .ORG $1000 + ;rest of the source code follows + + + + ;end of the source code + .ORG $17ff + .BYTE 0 + .END + +Now, to invoke TASM to generate the code in the binary format with all +unassigned bytes set to 00 (6502 BRK), do the following: + + tasm -65 -b -f00 test.asm + +Note that -b forces the -c option. + + TASM - Table Driven Assembler Version 2.9 Page 32 + +ERROR MESSAGES + + +Error Message Description +______________________________________________________________________________ +Binary operator where value expected. Two binary operators in a row + indicate a missing value. + +Cannot malloc for label storage Insufficient memory to store more + labels. See LIMITATIONS. + +Duplicate label. Duplicate label checks are + optionally enabled by the '-a' + option. + +File name too short A file name on the command line is + fewer than 3 characters. This is + done to prevent a garbled option + flag from being taken as a source + file, which in turn can result in + the source file taken as an object + file (which are truncated at start- + up time). + +Heap overflow on label definition. TASM was unable to allocate memory + to store the label. + +Invalid operand. No indirection for this instruction. + The first character of an operand + was a left parenthesis for an + instruction that does not + explicitly specify that as the + format. Some micros use the + parenthesis as an indicator of + indirection, but putting a layer of + parenthesis around an expression is + always a valid thing to do (as far + as the expression evaluator is + concerned). The test for this case + is only done if the -a4 option is + selected. See section on ASSEMBLY + CONTROL. + +Invalid token where value expected. Two binary operators in a row are + not allowed. + +Label too long Labels are limited to 31 characters. + +Label value misaligned The value of a label appears to + have a different value on the + second pass then it was computed to + have on the first pass. This is + generally due to Zero Page + Addressing mode problems with the + 6502 version of TASM. Labels that + are used in operands for statements + TASM - Table Driven Assembler Version 2.9 Page 33 + + that could utilize Zero Page + addressing mode should always be + defined before used as an operand. + +Label not found A label used in an expression was + not found in the current label + table. + +Label must pre-exist for SET. The SET directive can only be + applied to an existing label. + +Label table overflow To many labels have been + encountered. + +List file open error TASM was not able to open the + specified list file. + +Macro expansion too long. The expansion of a macro resulted + in a line that exceeded the maximum + length. + +Maximum number of macros exceeded To many macros (DEFINEs) have been + encountered. + +No END directive before EOF The source file did not have an END + directive in it. This is not fatal, + but may cause the last object file + record to be lost. + +No files specified TASM was invoked with no source + file specified. + +No such label yet defined. A SET directive was encountered for + a label not yet defined. The value + of labels that are modified by the + SET directive must already exist. + +No indirection for this instruction. A parenthesis was found around the + operand expression. This may + indicate an attempt to use + indirection where it is + inappropriate. + +Non-unary operator at start of expression A binary operator (such as '*') was + found at the beginning of an + expression. Some micros use '*' as + an indirection operator. Since it + is also a legititmate operator in + an expression, some ambiguity can + arise. If a particular + instruction/addressing mode does + not allow indirection, and a '*' is + placed in front of the associated + expression, the assembler will + assume this error. See the -a8 + option of ASSEMBLY CONTROL. + TASM - Table Driven Assembler Version 2.9 Page 34 + +Object file open error TASM was not able to open the + specified object file. + +Range of argument exceeded The value of an argument exceeds + the valid range for the current + instruction and addressing mode. + +Range of relative branch exceeded A branch instruction exceeds the + maximum range. + +Source file open error TASM was not able to open the + specified source file. + +Unrecognized directive A statement starting with a '.' or + '#' has a mnemonic that is not + defined as a directive. + +Unrecognized instruction A statement has an opcode mnemonic + that is not defined. + +Unrecognized argument A statement has an operand format + that is not defined. + +Unknown token Unexpected characters were found + while parsing an expression. + +Unused data in MS byte of argument. An instruction or directive used + the least significant byte of an + argurment and left the most + significant byte unused, but it was + non-zero. + +Unkown option Flag. Invalid option flag has been + specified on the command line. + invoke TASM with nothing on the + command line to see a list of valid + options. + + TASM - Table Driven Assembler Version 2.9 Page 35 + +BUGS AND LIMITATIONS + +Limitations and Specifications + TASM TASMB +_____________________________________________________________________________ +Maximum number of labels 2000 10000 +Maximum length of labels 32 characters +Maximum address space 64 Kbytes (65536 bytes) +Maximum number of nested INCLUDES 4 +Maximum length of TITLE string 79 characters +Maximum source line length 255 characters +Maximum length after macro expansion 255 characters +Maximum length of expressions 255 characters +Maximum length of pathnames 79 characters +Maximum length of command line 127 characters + +Maximum number of instructions (per table) 600 1000 +Maximum number of macros 1000 +Maximum number of macro arguments 10 +Maximum length of macro argument 16 characters +Heap size (for labels, macros, & buffers) 20000 bytes 60000 +Memory requirements 160K 256K + +Bugs + +1. The 8048 version of TASM does not check for use of memory beyond any +reasonable bounds (e.g. an 8048 has a maximum address space of 4 Kbytes but +TASM will let you pretend that you have 64 Kbytes). + +2. Expression evaluation has no operator precedence in effect which can +make for unexpected results if not explicitly grouped with parenthesis. + +3. First page of listing file will not show a user defined title (defined +via TITLE directive). + +4. TASM sometimes does not generate error messages for improperly formed +expressions. + + TASM - Table Driven Assembler Version 2.9 Page 36 + +APPENDIX A - ORDERING INFORMATION + +TASM is distributed as shareware. The shareware portion of the product may +be freely copied and used for evaluation purposes. Use of TASM beyond an +evaluation period of 90 days requires registration. Registered users receive +the following benefits: + + 1. The recent version of TASM. + 2. TASM source code (in C). + 3. Bound TASM manual. + 4. Telephone support. + 5. Knowledge that they are supporting the development of useful + but inexpensive software. + +DESCRIPTION UNIT PRICE PRICE +___________________________________________________________________________ + +TASM Registration (TASM disk, manual, & source) $40.00 _______ + +TASM Site Registration (for sites with multiple 90.00 _______ + users. Includes same materials as above.) + +TASM User's Manual (included above) 10.00 _______ + +TASM update for registered users 10.00 _______ + (latest disk (with source), and manual) + +Subtotal _______ + +Tax (Washington state residents add 8.2%) _______ + +Billing fee (for orders not accompanied by check) 10.00 _______ + +Foreign postage (outside North America) add $10.00 _______ + (Foreign orders must be in US funds drawn on a US bank) + +TOTAL (post paid) _______ + + +Which processors are of primary interest to you? __________________ +(This is for our information only. You will receive all current TASM tables). + +Shipping Address: Send check or money order to: + Speech Technology Incorporated +______________________________________________ Software Division + 837 Front Street South +______________________________________________ Issaquah, WA 98027 USA + +______________________________________________ + + \ No newline at end of file diff --git a/Z80 CPM and bootloader (basmon)/TASM80.TAB b/Z80 CPM and bootloader (basmon)/TASM80.TAB new file mode 100644 index 0000000..3c1d554 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/TASM80.TAB @@ -0,0 +1,579 @@ +"TASM Z80 Assembler. " +/**************************************************************************** +/* $Id: tasm80.tab 1.1 1993/07/31 01:12:40 toma Exp $ +/**************************************************************************** +/* This is the instruction set definition table +/* for the Z80 version of TASM. +/* Thomas N. Anderson, Speech Technology Incorporated +/* This table authored and submitted by Carl A. Wall, VE3APY. +/* +/* Class bits assigned as follows: +/* Bit-0 = Z80 (base instruction set) +/* Bit-1 = HD64180 (extended instructions) +/* See TASM manual for info on table structure. +/* +/*INSTR ARGS OP BYTES RULE CLASS SHIFT OR */ +/*-------------------------------------------*/ + +ADC A,(HL) 8E 1 NOP 1 +ADC A,(IX*) 8EDD 3 ZIX 1 +ADC A,(IY*) 8EFD 3 ZIX 1 +ADC A,A 8F 1 NOP 1 +ADC A,B 88 1 NOP 1 +ADC A,C 89 1 NOP 1 +ADC A,D 8A 1 NOP 1 +ADC A,E 8B 1 NOP 1 +ADC A,H 8C 1 NOP 1 +ADC A,L 8D 1 NOP 1 +ADC A,* CE 2 NOP 1 +ADC HL,BC 4AED 2 NOP 1 +ADC HL,DE 5AED 2 NOP 1 +ADC HL,HL 6AED 2 NOP 1 +ADC HL,SP 7AED 2 NOP 1 + +ADD A,(HL) 86 1 NOP 1 +ADD A,(IX*) 86DD 3 ZIX 1 +ADD A,(IY*) 86FD 3 ZIX 1 +ADD A,A 87 1 NOP 1 +ADD A,B 80 1 NOP 1 +ADD A,C 81 1 NOP 1 +ADD A,D 82 1 NOP 1 +ADD A,E 83 1 NOP 1 +ADD A,H 84 1 NOP 1 +ADD A,L 85 1 NOP 1 +ADD A,* C6 2 NOP 1 +ADD HL,BC 09 1 NOP 1 +ADD HL,DE 19 1 NOP 1 +ADD HL,HL 29 1 NOP 1 +ADD HL,SP 39 1 NOP 1 +ADD IX,BC 09DD 2 NOP 1 +ADD IX,DE 19DD 2 NOP 1 +ADD IX,IX 29DD 2 NOP 1 +ADD IX,SP 39DD 2 NOP 1 +ADD IY,BC 09FD 2 NOP 1 +ADD IY,DE 19FD 2 NOP 1 +ADD IY,IY 29FD 2 NOP 1 +ADD IY,SP 39FD 2 NOP 1 + +AND (HL) A6 1 NOP 1 +AND (IX*) A6DD 3 ZIX 1 +AND (IY*) A6FD 3 ZIX 1 +AND A A7 1 NOP 1 +AND B A0 1 NOP 1 +AND C A1 1 NOP 1 +AND D A2 1 NOP 1 +AND E A3 1 NOP 1 +AND H A4 1 NOP 1 +AND L A5 1 NOP 1 +AND * E6 2 NOP 1 + +BIT *,(HL) 46CB 2 ZBIT 1 +BIT *,(IX*) CBDD 4 ZBIT 1 0 4600 +BIT *,(IY*) CBFD 4 ZBIT 1 0 4600 +BIT *,A 47CB 2 ZBIT 1 +BIT *,B 40CB 2 ZBIT 1 +BIT *,C 41CB 2 ZBIT 1 +BIT *,D 42CB 2 ZBIT 1 +BIT *,E 43CB 2 ZBIT 1 +BIT *,H 44CB 2 ZBIT 1 +BIT *,L 45CB 2 ZBIT 1 + +CALL C,* DC 3 NOP 1 +CALL M,* FC 3 NOP 1 +CALL NC,* D4 3 NOP 1 +CALL NZ,* C4 3 NOP 1 +CALL P,* F4 3 NOP 1 +CALL PE,* EC 3 NOP 1 +CALL PO,* E4 3 NOP 1 +CALL Z,* CC 3 NOP 1 +CALL * CD 3 NOP 1 + +CCF "" 3F 1 NOP 1 + +CP (HL) BE 1 NOP 1 +CP (IX*) BEDD 3 ZIX 1 +CP (IY*) BEFD 3 ZIX 1 +CP A BF 1 NOP 1 +CP B B8 1 NOP 1 +CP C B9 1 NOP 1 +CP D BA 1 NOP 1 +CP E BB 1 NOP 1 +CP H BC 1 NOP 1 +CP L BD 1 NOP 1 +CP * FE 2 NOP 1 +CPD "" A9ED 2 NOP 1 +CPDR "" B9ED 2 NOP 1 +CPIR "" B1ED 2 NOP 1 +CPI "" A1ED 2 NOP 1 +CPL "" 2F 1 NOP 1 + +DAA "" 27 1 NOP 1 + +DEC (HL) 35 1 NOP 1 +DEC (IX*) 35DD 3 ZIX 1 +DEC (IY*) 35FD 3 ZIX 1 +DEC A 3D 1 NOP 1 +DEC B 05 1 NOP 1 +DEC BC 0B 1 NOP 1 +DEC C 0D 1 NOP 1 +DEC D 15 1 NOP 1 +DEC DE 1B 1 NOP 1 +DEC E 1D 1 NOP 1 +DEC H 25 1 NOP 1 +DEC HL 2B 1 NOP 1 +DEC IX 2BDD 2 NOP 1 +DEC IY 2BFD 2 NOP 1 +DEC L 2D 1 NOP 1 +DEC SP 3B 1 NOP 1 +DI "" F3 1 NOP 1 +DJNZ * 10 2 R1 1 + +EI "" FB 1 NOP 1 +EX (SP),HL E3 1 NOP 1 +EX (SP),IX E3DD 2 NOP 1 +EX (SP),IY E3FD 2 NOP 1 +EX AF,AF' 08 1 NOP 1 +EX DE,HL EB 1 NOP 1 +EXX "" D9 1 NOP 1 +HALT "" 76 1 NOP 1 + +IM 0 46ED 2 NOP 1 +IM 1 56ED 2 NOP 1 +IM 2 5EED 2 NOP 1 + +IN A,(C) 78ED 2 NOP 1 +IN B,(C) 40ED 2 NOP 1 +IN C,(C) 48ED 2 NOP 1 +IN D,(C) 50ED 2 NOP 1 +IN E,(C) 58ED 2 NOP 1 +IN H,(C) 60ED 2 NOP 1 +IN L,(C) 68ED 2 NOP 1 + +IN A,(*) DB 2 NOP 1 + +IN0 A,(*) 38ED 3 NOP 2 +IN0 B,(*) 00ED 3 NOP 2 +IN0 C,(*) 08ED 3 NOP 2 +IN0 D,(*) 10ED 3 NOP 2 +IN0 E,(*) 18ED 3 NOP 2 +IN0 H,(*) 20ED 3 NOP 2 +IN0 L,(*) 28ED 3 NOP 2 + +INC (HL) 34 1 NOP 1 +INC (IX*) 34DD 3 ZIX 1 +INC (IY*) 34FD 3 ZIX 1 +INC A 3C 1 NOP 1 +INC B 04 1 NOP 1 +INC BC 03 1 NOP 1 +INC C 0C 1 NOP 1 +INC D 14 1 NOP 1 +INC DE 13 1 NOP 1 +INC E 1C 1 NOP 1 +INC H 24 1 NOP 1 +INC HL 23 1 NOP 1 +INC IX 23DD 2 NOP 1 +INC IY 23FD 2 NOP 1 +INC L 2C 1 NOP 1 +INC SP 33 1 NOP 1 + + +IND "" AAED 2 NOP 1 +INDR "" BAED 2 NOP 1 +INI "" A2ED 2 NOP 1 +INIR "" B2ED 2 NOP 1 + +JP (HL) E9 1 NOP 1 +JP (IX) E9DD 2 NOP 1 +JP (IY) E9FD 2 NOP 1 +JP C,* DA 3 NOP 1 +JP M,* FA 3 NOP 1 +JP NC,* D2 3 NOP 1 +JP NZ,* C2 3 NOP 1 +JP P,* F2 3 NOP 1 +JP PE,* EA 3 NOP 1 +JP PO,* E2 3 NOP 1 +JP Z,* CA 3 NOP 1 +JP * C3 3 NOP 1 + +JR C,* 38 2 R1 1 +JR NC,* 30 2 R1 1 +JR NZ,* 20 2 R1 1 +JR Z,* 28 2 R1 1 +JR * 18 2 R1 1 + +LD (BC),A 02 1 NOP 1 +LD (DE),A 12 1 NOP 1 +LD (HL),A 77 1 NOP 1 +LD (HL),B 70 1 NOP 1 +LD (HL),C 71 1 NOP 1 +LD (HL),D 72 1 NOP 1 +LD (HL),E 73 1 NOP 1 +LD (HL),H 74 1 NOP 1 +LD (HL),L 75 1 NOP 1 +LD (HL),* 36 2 NOP 1 +LD (IX*),A 77DD 3 ZIX 1 +LD (IX*),B 70DD 3 ZIX 1 +LD (IX*),C 71DD 3 ZIX 1 +LD (IX*),D 72DD 3 ZIX 1 +LD (IX*),E 73DD 3 ZIX 1 +LD (IX*),H 74DD 3 ZIX 1 +LD (IX*),L 75DD 3 ZIX 1 +LD (IX*),* 36DD 4 ZIX 1 +LD (IY*),A 77FD 3 ZIX 1 +LD (IY*),B 70FD 3 ZIX 1 +LD (IY*),C 71FD 3 ZIX 1 +LD (IY*),D 72FD 3 ZIX 1 +LD (IY*),E 73FD 3 ZIX 1 +LD (IY*),H 74FD 3 ZIX 1 +LD (IY*),L 75FD 3 ZIX 1 +LD (IY*),* 36FD 4 ZIX 1 +LD (*),A 32 3 NOP 1 +LD (*),BC 43ED 4 NOP 1 +LD (*),DE 53ED 4 NOP 1 +LD (*),HL 22 3 NOP 1 +LD (*),IX 22DD 4 NOP 1 +LD (*),IY 22FD 4 NOP 1 +LD (*),SP 73ED 4 NOP 1 +LD A,(BC) 0A 1 NOP 1 +LD A,(DE) 1A 1 NOP 1 +LD A,(HL) 7E 1 NOP 1 +LD A,(IX*) 7EDD 3 ZIX 1 +LD A,(IY*) 7EFD 3 ZIX 1 +LD A,A 7F 1 NOP 1 +LD A,B 78 1 NOP 1 +LD A,C 79 1 NOP 1 +LD A,D 7A 1 NOP 1 +LD A,E 7B 1 NOP 1 +LD A,H 7C 1 NOP 1 +LD A,I 57ED 2 NOP 1 +LD A,L 7D 1 NOP 1 +LD A,R 5FED 2 NOP 1 +LD A,(*) 3A 3 NOP 1 +LD A,* 3E 2 NOP 1 +LD B,(HL) 46 1 NOP 1 +LD B,(IX*) 46DD 3 ZIX 1 +LD B,(IY*) 46FD 3 ZIX 1 +LD B,A 47 1 NOP 1 +LD B,B 40 1 NOP 1 +LD B,C 41 1 NOP 1 +LD B,D 42 1 NOP 1 +LD B,E 43 1 NOP 1 +LD B,H 44 1 NOP 1 +LD B,L 45 1 NOP 1 +LD B,* 06 2 NOP 1 +LD BC,(*) 4BED 4 NOP 1 +LD BC,* 01 3 NOP 1 +LD C,(HL) 4E 1 NOP 1 +LD C,(IX*) 4EDD 3 ZIX 1 +LD C,(IY*) 4EFD 3 ZIX 1 +LD C,A 4F 1 NOP 1 +LD C,B 48 1 NOP 1 +LD C,C 49 1 NOP 1 +LD C,D 4A 1 NOP 1 +LD C,E 4B 1 NOP 1 +LD C,H 4C 1 NOP 1 +LD C,L 4D 1 NOP 1 +LD C,* 0E 2 NOP 1 +LD D,(HL) 56 1 NOP 1 +LD D,(IX*) 56DD 3 ZIX 1 +LD D,(IY*) 56FD 3 ZIX 1 +LD D,A 57 1 NOP 1 +LD D,B 50 1 NOP 1 +LD D,C 51 1 NOP 1 +LD D,D 52 1 NOP 1 +LD D,E 53 1 NOP 1 +LD D,H 54 1 NOP 1 +LD D,L 55 1 NOP 1 +LD D,* 16 2 NOP 1 +LD DE,(*) 5BED 4 NOP 1 +LD DE,* 11 3 NOP 1 +LD E,(HL) 5E 1 NOP 1 +LD E,(IX*) 5EDD 3 ZIX 1 +LD E,(IY*) 5EFD 3 ZIX 1 +LD E,A 5F 1 NOP 1 +LD E,B 58 1 NOP 1 +LD E,C 59 1 NOP 1 +LD E,D 5A 1 NOP 1 +LD E,E 5B 1 NOP 1 +LD E,H 5C 1 NOP 1 +LD E,L 5D 1 NOP 1 +LD E,* 1E 2 NOP 1 +LD H,(HL) 66 1 NOP 1 +LD H,(IX*) 66DD 3 ZIX 1 +LD H,(IY*) 66FD 3 ZIX 1 +LD H,A 67 1 NOP 1 +LD H,B 60 1 NOP 1 +LD H,C 61 1 NOP 1 +LD H,D 62 1 NOP 1 +LD H,E 63 1 NOP 1 +LD H,H 64 1 NOP 1 +LD H,L 65 1 NOP 1 +LD H,* 26 2 NOP 1 +LD HL,(*) 2A 3 NOP 1 +LD HL,* 21 3 NOP 1 +LD I,A 47ED 2 NOP 1 +LD IX,(*) 2ADD 4 NOP 1 +LD IX,* 21DD 4 NOP 1 +LD IY,(*) 2AFD 4 NOP 1 +LD IY,* 21FD 4 NOP 1 +LD L,(HL) 6E 1 NOP 1 +LD L,(IX*) 6EDD 3 ZIX 1 +LD L,(IY*) 6EFD 3 ZIX 1 +LD L,A 6F 1 NOP 1 +LD L,B 68 1 NOP 1 +LD L,C 69 1 NOP 1 +LD L,D 6A 1 NOP 1 +LD L,E 6B 1 NOP 1 +LD L,H 6C 1 NOP 1 +LD L,L 6D 1 NOP 1 +LD L,* 2E 2 NOP 1 +LD R,A 4FED 2 NOP 1 +LD SP,(*) 7BED 4 NOP 1 +LD SP,HL F9 1 NOP 1 +LD SP,IX F9DD 2 NOP 1 +LD SP,IY F9FD 2 NOP 1 +LD SP,* 31 3 NOP 1 +LDD "" A8ED 2 NOP 1 +LDDR "" B8ED 2 NOP 1 +LDI "" A0ED 2 NOP 1 +LDIR "" B0ED 2 NOP 1 +NEG "" 44ED 2 NOP 1 +NOP "" 00 1 NOP 1 + +MLT BC 4CED 2 NOP 2 +MLT DE 5CED 2 NOP 2 +MLT HL 6CED 2 NOP 2 +MLT SP 7CED 2 NOP 2 + +OR (HL) B6 1 NOP 1 +OR (IX*) B6DD 3 ZIX 1 +OR (IY*) B6FD 3 ZIX 1 +OR A B7 1 NOP 1 +OR B B0 1 NOP 1 +OR C B1 1 NOP 1 +OR D B2 1 NOP 1 +OR E B3 1 NOP 1 +OR H B4 1 NOP 1 +OR L B5 1 NOP 1 +OR * F6 2 NOP 1 + +OTDM "" 8BED 2 NOP 2 +OTDMR "" 9BED 2 NOP 2 +OTDR "" BBED 2 NOP 1 +OTIM "" 83ED 2 NOP 2 +OTIMR "" 93ED 2 NOP 2 +OTIR "" B3ED 2 NOP 1 + +OUT (C),A 79ED 2 NOP 1 +OUT (C),B 41ED 2 NOP 1 +OUT (C),C 49ED 2 NOP 1 +OUT (C),D 51ED 2 NOP 1 +OUT (C),E 59ED 2 NOP 1 +OUT (C),H 61ED 2 NOP 1 +OUT (C),L 69ED 2 NOP 1 +OUT (*),A D3 2 NOP 1 + +OUT0 (*),A 39ED 3 NOP 2 +OUT0 (*),B 01ED 3 NOP 2 +OUT0 (*),C 09ED 3 NOP 2 +OUT0 (*),D 11ED 3 NOP 2 +OUT0 (*),E 19ED 3 NOP 2 +OUT0 (*),H 21ED 3 NOP 2 +OUT0 (*),L 29ED 3 NOP 2 + +OUTD "" ABED 2 NOP 1 +OUTI "" A3ED 2 NOP 1 + +POP AF F1 1 NOP 1 +POP BC C1 1 NOP 1 +POP DE D1 1 NOP 1 +POP HL E1 1 NOP 1 +POP IX E1DD 2 NOP 1 +POP IY E1FD 2 NOP 1 + +PUSH AF F5 1 NOP 1 +PUSH BC C5 1 NOP 1 +PUSH DE D5 1 NOP 1 +PUSH HL E5 1 NOP 1 +PUSH IX E5DD 2 NOP 1 +PUSH IY E5FD 2 NOP 1 + +RES *,(HL) 86CB 2 ZBIT 1 +RES *,(IX*) CBDD 4 ZBIT 1 0 8600 +RES *,(IY*) CBFD 4 ZBIT 1 0 8600 +RES *,A 87CB 2 ZBIT 1 +RES *,B 80CB 2 ZBIT 1 +RES *,C 81CB 2 ZBIT 1 +RES *,D 82CB 2 ZBIT 1 +RES *,E 83CB 2 ZBIT 1 +RES *,H 84CB 2 ZBIT 1 +RES *,L 85CB 2 ZBIT 1 + +RET "" C9 1 NOP 1 +RET C D8 1 NOP 1 +RET M F8 1 NOP 1 +RET NC D0 1 NOP 1 +RET NZ C0 1 NOP 1 +RET P F0 1 NOP 1 +RET PE E8 1 NOP 1 +RET PO E0 1 NOP 1 +RET Z C8 1 NOP 1 +RETI "" 4DED 2 NOP 1 +RETN "" 45ED 2 NOP 1 + +RL (HL) 16CB 2 NOP 1 +RL (IX*) CBDD 4 ZIX 1 0 1600 +RL (IY*) CBFD 4 ZIX 1 0 1600 +RL A 17CB 2 NOP 1 +RL B 10CB 2 NOP 1 +RL C 11CB 2 NOP 1 +RL D 12CB 2 NOP 1 +RL E 13CB 2 NOP 1 +RL H 14CB 2 NOP 1 +RL L 15CB 2 NOP 1 +RLA "" 17 1 NOP 1 + +RLC (HL) 06CB 2 NOP 1 +RLC (IX*) CBDD 4 ZIX 1 0 0600 +RLC (IY*) CBFD 4 ZIX 1 0 0600 +RLC A 07CB 2 NOP 1 +RLC B 00CB 2 NOP 1 +RLC C 01CB 2 NOP 1 +RLC D 02CB 2 NOP 1 +RLC E 03CB 2 NOP 1 +RLC H 04CB 2 NOP 1 +RLC L 05CB 2 NOP 1 +RLCA "" 07 1 NOP 1 +RLD "" 6FED 2 NOP 1 + +RR (HL) 1ECB 2 NOP 1 +RR (IX*) CBDD 4 ZIX 1 0 1E00 +RR (IY*) CBFD 4 ZIX 1 0 1E00 +RR A 1FCB 2 NOP 1 +RR B 18CB 2 NOP 1 +RR C 19CB 2 NOP 1 +RR D 1ACB 2 NOP 1 +RR E 1BCB 2 NOP 1 +RR H 1CCB 2 NOP 1 +RR L 1DCB 2 NOP 1 +RRA "" 1F 1 NOP 1 +RRC (HL) 0ECB 2 NOP 1 +RRC (IX*) CBDD 4 ZIX 1 0 0E00 +RRC (IY*) CBFD 4 ZIX 1 0 0E00 +RRC A 0FCB 2 NOP 1 +RRC B 08CB 2 NOP 1 +RRC C 09CB 2 NOP 1 +RRC D 0ACB 2 NOP 1 +RRC E 0BCB 2 NOP 1 +RRC H 0CCB 2 NOP 1 +RRC L 0DCB 2 NOP 1 +RRCA "" 0F 1 NOP 1 +RRD "" 67ED 2 NOP 1 + +RST 00H C7 1 NOP 1 +RST 08H CF 1 NOP 1 +RST 10H D7 1 NOP 1 +RST 18H DF 1 NOP 1 +RST 20H E7 1 NOP 1 +RST 28H EF 1 NOP 1 +RST 30H F7 1 NOP 1 +RST 38H FF 1 NOP 1 + +SBC A,(HL) 9E 1 NOP 1 +SBC A,(IX*) 9EDD 3 ZIX 1 +SBC A,(IY*) 9EFD 3 ZIX 1 +SBC A,A 9F 1 NOP 1 +SBC A,B 98 1 NOP 1 +SBC A,C 99 1 NOP 1 +SBC A,D 9A 1 NOP 1 +SBC A,E 9B 1 NOP 1 +SBC A,H 9C 1 NOP 1 +SBC A,L 9D 1 NOP 1 +SBC HL,BC 42ED 2 NOP 1 +SBC HL,DE 52ED 2 NOP 1 +SBC HL,HL 62ED 2 NOP 1 +SBC HL,SP 72ED 2 NOP 1 +SBC A,* DE 2 NOP 1 +SCF "" 37 1 NOP 1 + +SET *,(HL) C6CB 2 ZBIT 1 +SET *,(IX*) CBDD 4 ZBIT 1 0 C600 +SET *,(IY*) CBFD 4 ZBIT 1 0 C600 +SET *,A C7CB 2 ZBIT 1 +SET *,B C0CB 2 ZBIT 1 +SET *,C C1CB 2 ZBIT 1 +SET *,D C2CB 2 ZBIT 1 +SET *,E C3CB 2 ZBIT 1 +SET *,H C4CB 2 ZBIT 1 +SET *,L C5CB 2 ZBIT 1 + +SLA (HL) 26CB 2 NOP 1 +SLA (IX*) CBDD 4 ZIX 1 0 2600 +SLA (IY*) CBFD 4 ZIX 1 0 2600 +SLA A 27CB 2 NOP 1 +SLA B 20CB 2 NOP 1 +SLA C 21CB 2 NOP 1 +SLA D 22CB 2 NOP 1 +SLA E 23CB 2 NOP 1 +SLA H 24CB 2 NOP 1 +SLA L 25CB 2 NOP 1 + +SLP "" 76ED 2 NOP 2 + +SRA (HL) 2ECB 2 NOP 1 +SRA (IX*) CBDD 4 ZIX 1 0 2E00 +SRA (IY*) CBFD 4 ZIX 1 0 2E00 +SRA A 2FCB 2 NOP 1 +SRA B 28CB 2 NOP 1 +SRA C 29CB 2 NOP 1 +SRA D 2ACB 2 NOP 1 +SRA E 2BCB 2 NOP 1 +SRA H 2CCB 2 NOP 1 +SRA L 2DCB 2 NOP 1 + +SRL (HL) 3ECB 2 NOP 1 +SRL (IX*) CBDD 4 ZIX 1 0 3E00 +SRL (IY*) CBFD 4 ZIX 1 0 3E00 +SRL A 3FCB 2 NOP 1 +SRL B 38CB 2 NOP 1 +SRL C 39CB 2 NOP 1 +SRL D 3ACB 2 NOP 1 +SRL E 3BCB 2 NOP 1 +SRL H 3CCB 2 NOP 1 +SRL L 3DCB 2 NOP 1 + +SUB (HL) 96 1 NOP 1 +SUB (IX*) 96DD 3 ZIX 1 +SUB (IY*) 96FD 3 ZIX 1 +SUB A 97 1 NOP 1 +SUB B 90 1 NOP 1 +SUB C 91 1 NOP 1 +SUB D 92 1 NOP 1 +SUB E 93 1 NOP 1 +SUB H 94 1 NOP 1 +SUB L 95 1 NOP 1 +SUB * D6 2 NOP 1 + +TST A 3CED 2 NOP 2 +TST B 04ED 2 NOP 2 +TST C 0CED 2 NOP 2 +TST D 14ED 2 NOP 2 +TST E 1CED 2 NOP 2 +TST H 24ED 2 NOP 2 +TST L 2CED 2 NOP 2 +TST (HL) 34ED 2 NOP 2 +TST * 64ED 3 NOP 2 + +TSTIO * 74ED 3 NOP 2 + +XOR (HL) AE 1 NOP 1 +XOR (IX*) AEDD 3 ZIX 1 +XOR (IY*) AEFD 3 ZIX 1 +XOR A AF 1 NOP 1 +XOR B A8 1 NOP 1 +XOR C A9 1 NOP 1 +XOR D AA 1 NOP 1 +XOR E AB 1 NOP 1 +XOR H AC 1 NOP 1 +XOR L AD 1 NOP 1 +XOR * EE 2 NOP 1 diff --git a/Z80 CPM and bootloader (basmon)/TASM_RELNOTES.TXT b/Z80 CPM and bootloader (basmon)/TASM_RELNOTES.TXT new file mode 100644 index 0000000..800f756 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/TASM_RELNOTES.TXT @@ -0,0 +1,199 @@ +TASM RELEASE NOTES + +RELEASE DATE/VERSION DESCRIPTION +----------------------------------------------------------------------- +10/01/85 Version 2.0 First version with external table def files. + +01/01/86 Version 2.1 Added '*=' and '=' directives as + alternatives to .ORG and .EQU (for + more complete MOS Technology compatibility). + Enhanced parsing algorithm so it can + deal with more than one variable expression. + Added -d option + +02/14/86 Version 2.2 Modified so instruction set definition + tables don't need to be compiled in. + Added 8051 tables. + Increased the number of labels allowed. + +03/31/87 Version 2.3 Fixed bug that prevented location 0xffff + from being used and written to object file. + Most changes in wrtobj() and pr_hextab(). + +05/01/87 Version 2.4 Added multiple byte opcode support. + Added shift/or operation capability to + args from instruction set definition table. + Converted to MS C version 3.0 + Added hashing to instruction set table + lookups to speed up. + +11/01/87 Version 2.5 Added DB and DW directives. + Added escape capability in TEXT strings. + Fixed inst_lookup function to treat the + multiple wild card case a little better + Added 8080/8085 and Z80 tables. + Added sorting on label table. + Increased size of read buffer. + Speed enhancements. + Added DEFCONT (macro continuation) directive. + Converted to Microsoft C 5.0 compiler. + Added 6805 table (and related modops). + Added Z80 bit modop. + Minor speed up. + Fixed bug that enters infinite loop + when a macro invocation has no closing paren. + Added some three arg MODOPs. + +8/15/88 Version 2.6.1 Added CODES/NOCODES directives + Fixed bug preventing directives in multiple + statement lines. + 2.6.2 Added COMB_NIBBLE and COMB_NIBBLE_SWAP MODOPS + +2/1/89 Version 2.7 Removed ad hoc heap and now use malloc() + Added MSFIRST and LSFIRST directives. + Added EXPORT directive. + Added symbol table file (-s flag). + Added NSEG/CSEG/BSEG/DSEG/XSEG directives + and the SYM/AVSYM directives to support + the Avocet avsim51 simulator. + Added support for TMS320. + Added -r flag to set read buffer size. + Converted expression evaluation from + signed 16 bit to signed 32 bit (enabling + apparent ability to use signed or unsigned + 16 bit values). + +4/20/89 Version 2.7.1 Return 0x20000 for undefined labels so that + (label+x) type stuff won't confuse zero + page addressing. + Added duplicate label error message on pass 1. + +6/20/89 Version 2.7.2 Improved macro expansion capability. + No expansion in comments. + Context sensitive identifiers. + Revised exit codes. + +6/27/89 Version 2.7.3 Added -a flag for strict error checking: + (1) No outer parens around expressions. + (2) Error message if unused argbytes remain + (3) Duplicate labels + Fixed so ']' can terminate expressions. + Removed parse() from tasm.c + +8/19/89 Version 2.7.4 Added Motorola hex object format. + Fixed bug that complained when \ immediately + followed a opcode with no args. + Slightly improved error reporting (Errorbuf). + +10/31/89 Version 2.7.5 Added TMS7000 support. + Fixed argv[] bug (only dimensioned to 10 in pass1. + +12/23/89 Version 2.7.6 Improved handling of % (modulo vs binary + prefix ambiguity). + Fixed list so lines with more than + 6 bytes go on second line. + +03/04/90 Version 2.7.7 Fixed bug that left off 2 bytes if ORG + went backwards and all 64K was used. + Added a command line option to ignore + case on labels. + Added a couple MODOP rules for TMS9900. + Allow double quoted text strings for BYTE. + +04/15/90 Version 2.7.8 Fixed expression evaluator bug (paren popping) + and changed expression evaluator to a more + conventional left to right evaluation order. + Added TURBOC ifdef's (from Lance Jump). + +08/20/90 Version 2.8 Primarily a documentation update. + Added error check for AJMP/ACALL off of + current 2K block (8051). + +10/15/90 Version 2.8.1 Minor speed up in label searching. + Fixed word addressing for TMS320 + Version 2.8.2 Local labels. + More label table format options (long form + suppress local labels). + +11/30/90 Version 2.8.3 Turbo C conversion. + DS directive added. + +12/27/90 Version 2.8.4 Added COMMENTCHAR directive to change the + comment indicator in the first column. + This was done to support the assembly + files from the small C compiler (sc11) + for the 68CH11. + +02/14/91 Version 2.8.5 Added LOCALLABELCHAR directive to + override the default "_" as the + prefix for local labels. + +03/18/91 Version 2.8.6 Added some MODOPs in support of TMS320C25 + +04/20/91 Version 2.8.7 Fixed sign extend bug in CSWAP modop. + Increased MAXLABS to 10000 for big version. + +05/05/91 Version 2.8.8 Fixed pointer bug in debug output in sort_labels(). + +05/20/91 Version 2.9 TMS320C25 table along with some MODOP enhancements + for it. + TASMTABS.DOC updated (but not TASM.DOC) + +08/09/91 Version 2.9.1 Nested conditionals. + +04/01/92 Version 2.9.2 Fixed long label clobber problem in + find_label() and save_label. Syntax + errors could result in a comment line + after an instruction being lumped together + with a label resulting in a long label. + The label functions were not testing for + labels that exceed the specified size. + Added CHK directive. + Added REL3 MODOD to support uPD75xxx. + Delinting and more ANSIfication. + Modifications due to feedback from B Provo: + Added FILL directive. + Allow multiple labels for EXPORT directive. + Allow address with END directive. + TASM.DOC update + +11/25/92 Version 2.9.3 Improved error reporting for mismatched quotes. + Disallow the single quote character constants. + Convert to BCC++ 3.1 + Provide filename,linenum on all error messages. + Modify format of error messages for compatibility + with the Brief editor. + Added ECHO directive to send output to console. + Performance improvements in macro processing. + "Type Safe" conversion (compatible with C++). + Improved error reporting for imbalanced ifdefs. + + +01/29/93 Version 2.9.4 Added rules for 8096 (I1,I2,I3,I4,I5,I6). + Generate error message on forward reference + in EQUate statements. + Eliminated -a option for enabling the detection + of branches of 2K page for 8051. This + is now built into the table. + Allow white space in double quotes for BYTE + directive. This previously worked for TEXT, + but not BYTE. + Fixed defect with Z80 4 byte indexed instructions. + Fixed macro defect. If the macro definition has + args but the invocation does not some garbage + gets expanded into the source line. + Z80 OTDR opcode was incorrect. + Z80 IN0/OUT0/INA instructions did not require + the parens around the args. + Some experimental support for windows verson of TASM. + +10/24/93 Version 3.0 Documentation update. TASM.DOC, TASMTABS.DOC + and RELNOTES.DOC updated, but the functionality + remains unchanged from version 2.9.4. + +06/16/94 Version 3.0.1 SPR 1006: Multiple macros on the same line + SPR 1007: -c with >8000h bytes used goes bonkers + SPR 1009: waddr correction for BLOCK/DS + SPR 1011: Escaped quotes in TEXT + + \ No newline at end of file diff --git a/Z80 CPM and bootloader (basmon)/_ASSEMBLE.BAT b/Z80 CPM and bootloader (basmon)/_ASSEMBLE.BAT new file mode 100644 index 0000000..c7b6ace --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/_ASSEMBLE.BAT @@ -0,0 +1,8 @@ +tasm -80 source\basMon.asm hexFiles\basMon.hex +tasm -80 source\cbios128.asm hexFiles\cbios128.hex +tasm -80 source\cpm22.asm hexFiles\cpm22.hex +tasm -80 source\form128.asm hexFiles\form128.hex +tasm -80 source\putsys.asm hexFiles\putsys.hex +tasm -80 source\download.asm hexFiles\download.hex + +pause diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/BASMON.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/BASMON.HEX new file mode 100644 index 0000000..7344ae9 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/BASMON.HEX @@ -0,0 +1,343 @@ +:0430040000000000C8 +:18000000F3C3940000000000C332000000000000C31B000000000000CB +:18001800C35C003A0030FE002008CD630028FBDB81C9CD6A0028FBDB74 +:1800300083C9F53A0030FE00200D1801F5CD500028FBF1D381C9F5CDC4 +:18004800560028FBF1D383C9DB800FCB47C9DB820FCB47C93A0030FE23 +:18006000002007DB80E601FE00C9DB82E601FE00C9D7FE0A28FBFE1B32 +:1800780020023E03C9FE0D280AFE0C2804FE203801CFC93E0DCF3E0A80 +:18009000CF3E0DC93128303E95D380D3823E00320030216503CD1B015F +:1800A8003E01320030216503CD1B01CD6300280F3E00320030CD1B003E +:1800C000FE20C2B3001814CD6A0028E73E01320030CD1B00FE20C2B307 +:1800D8000018003E0CCD3C00CD4600CD2201219602CD1B0121EC00E50E +:1800F000CD22013E3ECFCD7100FE2038F9FE3ACA9901CD7D00E65FFE07 +:1801080042CADA01FE47CA9301FE58CAF1013E3FCF18D57EB7C8CF231B +:1801200018F93E0DCF3E0ACFC9CD7100FE03C8FE2038F6C9CD29014762 +:18013800CD29014FCD73014F7B915F79C9210000CD8C01FE0D200E3741 +:18015000C9210000CD8C01FE0DC8FE2CC8FE0328EE29292929D630FECF +:180168000A3802D607E60F856F18E178D630FE0A3802D60707070707C3 +:180180004779D630FE0A3802D60780C9CD7100CD7D00C9CD4501D8E518 +:18019800C91E00CD340157CD340167CD34016FCD3401FE012009CD340A +:1801B000017BA7281E18157AA7280BCD340177233E2ECF1518F1CD3457 +:1801C800017BA7C8215403CD1B01C9218A03CD1B01C9214203CD1B015B +:1801E000CD2901C8E65FFE43CA9503FE57CA9803C9210302CD1B01CD01 +:1801F8002901C8E65FFE59CA2402C90D0A426F6F742043502F4D3F008F +:180210000D0A4C6F6164696E672043502F4D2E2E2E0D0A00211002CD31 +:180228001B0106183E003204303205303206303207302100D022023063 +:18024000CD6E021100022A0230192202303A04303C32043010EA3A0049 +:1802580030F52AFEFFE93A0630D38C3A0530D38B3A0430D38AC9F5C56F +:18027000E5DB89FE8020FACD5E023E00D3890E040680DB89FEE020FADA +:18028800DB8877230520F30D20EEE1C1F1C943502F4D20426F6F7420EF +:1802A000524F4D20322E3020627920472E20536561726C650D0A0D0A6E +:1802B8004243206F72204257202D20524F4D20424153494320436F6CD4 +:1802D000642F5761726D0D0A5820202020202020202D20426F6F74207C +:1802E80043502F4D20286C6F61642024443030302D2446464646290D50 +:180300000A3A6E6E6E6E2E2E2E202D204C6F616420496E74656C2D48E1 +:1803180065782066696C65207265636F72640D0A476E6E6E6E2020201B +:18033000202D2052756E206C6F63206E6E6E6E0D0A000D0A436F6C642D +:18034800206F72207761726D3F0D0A00436865636B73756D20657272D3 +:180360006F720D0A000C5072657373205B53504143455D20746F2061AC +:180378006374697661746520636F6E736F6C650D0A00436F6D706C65F3 +:1803900074650D0A00C39B03C33904DD210000C3A6034C0CC21321B09C +:1803A80030F9C3E11F117306066321B0301A77231305C2B503F9CD74DD +:1803C00008CD420E325A3132A931218804CDE014CD9108CD9A0BB7C278 +:1803D800EF03210D32237CB5CA01047E472F77BE70CADD03C30104CDC0 +:1803F000660CB7C24207EB2B3ED94677BE70C2CA032B110C32CD0A0ABF +:18040800DACA0311CEFF225F3119220A31CD4F082A0A3111EFFF19117D +:18042000A9317D936F7C9A67E5215104CDE014E1CD831B214204CDE072 +:1804380014311631CD7408C38D0720427974657320667265650D0A0080 +:18045000005A38302042415349432056657220342E37620D0A436F70AF +:18046800797269676874202843292031393738206279204D6963726F23 +:18048000736F66740D0A00004D656D6F727920746F7000F819BC1A0EB0 +:180498001AB330A0132517CE13821C611D9D18D01CD61DDC1D3D1E5229 +:1804B0001E7917BD1E013152166A14EC1661167216DF1E721F8216B2BA +:1804C80016BC16C54E44C64F52CE455854C4415441C94E505554C44900 +:1804E0004DD2454144CC4554C74F544FD2554EC946D24553544F5245D5 +:1804F800C74F535542D2455455524ED2454DD3544F50CF5554CF4ECEFF +:18051000554C4CD7414954C44546D04F4B45C44F4B45D3435245454E50 +:18052800CC494E4553C34C53D749445448CD4F4E49544F52D34554D278 +:1805400045534554D052494E54C34F4E54CC495354C34C454152C34CFF +:180558004F4144C353415645CE4557D4414228D44FC64ED3504328D443 +:1805700048454ECE4F54D3544550ABADAAAFDEC14E44CF52BEBDBCD35E +:18058800474EC94E54C14253D55352C65245C94E50D04F53D35152D20D +:1805A0004E44CC4F47C55850C34F53D3494ED4414EC1544ED045454BA8 +:1805B800C445454BD04F494E54CC454ED3545224D6414CC15343C348C7 +:1805D0005224C8455824C2494E24CC45465424D24947485424CD49444C +:1805E8002480E40BE10ABC0F310DC30EF811F20E480DEE0CD10CC00DA1 +:18060000AA0BDD0C0C0D330DE20B3117A20D230C3717D6138017C81E24 +:18061800330DAE1EA11EA61EDE1F04310731E40D100C560A8B0C330D8D +:18063000330D4E08796A1B799E177CDC187C3D197F8B1C505111465040 +:18064800114E46534E52474F4446434F564F4D554C425344442F3049F8 +:1806600044544D4F534C535354434E55464D4F4858424EC33904C36199 +:180678000CD300C9D6006F7CDE006778DE00473E00C9000000354ACACF +:1806900099391C76982295B3980ADD479853D199990A1A9F9865BCCDEF +:1806A80098D6773E9852C74F80DB00C901FF1C000014001400000000AF +:1806C00000C38709C30000C30000C300000D32FEFFAA31204572726FB7 +:1806D800720020696E20004F6B0D0A0000427265616B00210400397EEF +:1806F00023FE81C04E234623E569607AB3EBCA0507EBCD0A0A010D0040 +:18070800E1C809C3EF06CD2807C5E3C1CD0A0A7E02C80B2BC31407E5E8 +:180720002A8A31060009093EE53ED0956F3EFF9CDA37076739E1D81E27 +:180738000CC356072A7931220C311E02011E14011E00011E12011E2266 +:18075000011E0A011E18CD740832F530CD350E214906573E3FCD1B0A4B +:18076800197ECD1B0ACD9A0BCD1B0A21D306CDE0142A0C3111FEFFCD8F +:180780000A0ACAA6037CA53CC47B1B3EC1AF32F530CD350E21DF06CD3B +:18079800E01421FFFF220C31CD8709DA9A07CD9A0B3C3DCA9A07F5CDE7 +:1807B000660CD5CD9E0847D1F1D27A0BD5C5AF327C31CD9A0BB7F5CD04 +:1807C8002E08DAD307F1F5CA070DB7C5D2EA07EB2A86311A020313CD61 +:1807E0000A0AC2DB076069228631D1F1CA11082A8631E3C109E5CD0EB4 +:1807F80007E1228631EB74D12323732372231111311A772313B7C209EB +:1808100008CD5A0823EB626B7E23B6CA9A07232323AFBE23C22208EB2C +:18082800732372C316082A0E31444D7E23B62BC823237E23666FCD0AF8 +:180840000A60697E23666F3FC83FD0C33108C02A0E31AF772377232217 +:1808580086312A0E312B227E312A5F31227331AFCDAA0B2A8631228830 +:1808700031228A31C12A0A31F9216331226131AF6F67228431327B31A0 +:18088800228E31E5C52A7E31C93E3FCD1B0A3E20CD1B0AC3FE30AF329A +:1808A0005E310E051111317EFE20CA260947FE22CA4609B7CA4D093A25 +:1808B8005E31B77EC22609FE3F3E9ECA26097EFE30DAD108FE3CDA26C8 +:1808D00009D511CA04C5012209C5067F7EFE61DAEA08FE7BD2EA08E64C +:1808E8005F774EEB23B6F2EC08047EE67FC8B9C2EC08EBE5131AB7FA5E +:180900001E094F78FE88C20D09CD9A0B2B237EFE61DA1609E65FB9CA35 +:18091800FC08E1C3EA0848F1EBC9EB79C1D12312130CD63ACA3409FEE1 +:1809300049C23709325E31D654C2A708477EB7CA4D09B8CA2609231286 +:180948000C13C33D092110311213121312C93AF430B73E0032F430C27D +:180960006A0905CA8709CD1B0A3E052BCA7E097ECD1B0AC39009052B00 +:18097800CD1B0AC29009CD1B0ACD420EC387092111310601AF32F43049 +:18099000CD450A4FFE7FCA56093AF430B7CAA9093E00CD1B0AAF32F4A8 +:1809A8003079FE07CAED09FE03CC420E37C8FE0DCA3D0EFE15CA810926 +:1809C000FE40CA7E09FE5FCA7609FE08CA7609FE12C2E809C5D5E53623 +:1809D80000CDF21F211131CDE014E1D1C1C39009FE20DA900978FE49E6 +:1809F0003E07D2020A7971327C312304CD1B0AC39009CD1B0A3E08C393 +:180A0800FC097C92C07D93C97EE3BE23E3CA9A0BC34207F53AF530B77F +:180A2000C21515F1C5F5FE20DA3F0A3AF230473A5B3104CA3B0A05B8AD +:180A3800CC420E3C325B31F1C1CDDB1FC9CD9F1EE67FFE0FC03AF53033 +:180A50002F32F530AFC9CD660CC0C1CD2E08C5CDAC0AE14E2346237852 +:180A6800B1CA8D07CDB50ACDC50BC5CD420E5E235623E5EBCD831B3EE9 +:180A800020E1CD1B0A7EB723CA620AF2820AD67F4F11CB041A13B7F205 +:180A9800940A0DC2940AE67FCD1B0A1A13B7F29E0AC3850AE52AF830DD +:180AB00022F630E1C9E5D52AF63011FFFFED5A22F630D1E1F0E52AF8EB +:180AC8003022F630CD9F1EFE03CAD80AE1C3B50A2AF83022F630C33C6B +:180AE000043E64327B31CD480DC1E5CD310D22773121020039CDEF06BF +:180AF800D1C2110B09D52B562B5E2323E52A7731CD0A0AE1C2F50AD1FE +:180B1000F9EB0E08CD1F07E52A7731E3E52A0C31E3CD0A10CD100AA6A3 +:180B2800CD0710E5CD351AE1C5D5010081515A7EFEAB3E01C24D0BCDDB +:180B40009A0BCD0710E5CD351ACDE919E1C5D5F533E52A7E31E3068179 +:180B5800C533CDC50B227E317EFE3ACA7A0BB7C24207237E23B6CAEC28 +:180B70000B235E2356EB220C31EBCD9A0B115A0BD5C8D680DA480DFE2B +:180B880025D24207074F0600EB21EA05094E2346C5EB237EFE3AD0FEA7 +:180BA00020CA9A0BFE303F3C3DC9EB2A0E31CABF0BEBCD660CE5CD2E0D +:180BB800086069D1D2070D2B228C31EBC9DFC8D7FE1B2811FE03280DD9 +:180BD000FE13C0D7FE11C8FE03280718F63EFF32FD30C0F6C0227E316D +:180BE80021F6FFC12A0C31F57DA43CCAFF0B2282312A7E31228431AF5D +:180C000032F530CD350EF121E506C27607C38D072A84317CB51E20CACA +:180C18005607EB2A8231220C31EBC9CD6817C032F130C9E52AFA300625 +:180C3000004F0922FA30E1C97EFE41D8FE5B3FC9CD9A0BCD0710CDE95C +:180C480019FA610C3A9731FE90DA911A018090110000E5CD641AE1517B +:180C6000C81E08C356072B110000CD9A0BD0E5F5219819CD0A0ADA4247 +:180C780007626B19291929F1D6305F160019EBE1C36A0CCA5E08CD4342 +:180C90000C2BCD9A0BE52A5F31CAAE0CE1CD100A2CD5CD430C2BCD9A09 +:180CA8000BC24207E3EB7D935F7C9A57DA3707E52A863101280009CD97 +:180CC0000A0AD23707EB220A31E1225F31E1C35E08CA5A08CD5E0801B3 +:180CD8005A0BC3ED0C0E03CD1F07C1E5E52A0C31E33E8CF533C5CD6620 +:180CF0000CCD330DE52A0C31CD0A0AE123DC3108D42E0860692BD81E99 +:180D08000EC35607C016FFCDEB06F9FE8C1E04C25607E1220C31237C6F +:180D2000B5C22B0D3A7C31B7C28C07215A0BE33EE1013A0E00060079C9 +:180D380048477EB7C8B8C823FE22CA370DC33A0DCDFD11CD100AB4D5F1 +:180D50003A5D31F5CD1910F1E3227E311FCD0C10CA9B0DE52A9431E500 +:180D680023235E23562A0E31CD0A0AD28A0D2A0A31CD0A0AD1D2920D1B +:180D8000216F31CD0A0AD2920D3ED1CD4116EBCD7A14CD4116E1CD44B9 +:180D98001AE1C9E5CD411AD1E1C9CD68177E47FE8CCAB10DCD100A8865 +:180DB0002B4B0D78CA820BCD670CFE2CC0C3B20DCD19107EFE88CACE9B +:180DC8000DCD100AA92BCD0A10CDE919CA330DCD9A0BDAEE0CC3810BF6 +:180DE0002BCD9A0BCA420EC8FEA5CA750EFEA8CA750EE5FE2CCA5E0E54 +:180DF800FE3BCA980EC1CD1910E53A5D31B7C22E0ECD8E1BCD9E1436F6 +:180E1000202A9431342A94313AF2304704CA2A0E043A5B31863DB8D4D6 +:180E2800420ECDE314AFC4E314E1C3E00D3A5B31B7C8C3420E360021F4 +:180E400010313E0DCD1B0A3E0ACD1B0AAF325B313AF1303DC8F5AFCDA4 +:180E58001B0AF1C3530E3AF330473A5B31B8D4420ED2980ED60ED26C68 +:180E70000E2FC38D0EF5CD6517CD100A292BF1D6A8E5CA880E3A5B31DC +:180E88002F83D2980E3C473E20CD1B0A05C2910EE1CD9A0BC3E70D3FA6 +:180EA0005265646F2066726F6D2073746172740D0A003A7D31B7C23CDA +:180EB80007C1219F0ECDE014C38D08CD4B147EFE223E0032F530C2DD75 +:180ED0000ECD9F14CD100A3BE5CDE3143EE5CD9108C1DAE90B237EB741 +:180EE8002BC5CA300D362CC3F70EE52A8C31F6AF327D31E3C3030FCDFB +:180F0000100A2CCDFD11E3D57EFE2CCA2B0F3A7D31B7C2980F3E3FCD02 +:180F18001B0ACD9108D1C1DAE90B237EB72BC5CA300DD53A5D31B7CA69 +:180F3000550FCD9A0B5747FE22CA490F3A7D31B757CA460F163A062C5C +:180F48002BCDA214EB21600FE3D5C3630DCD9A0BCDF01AE3CD411AE148 +:180F60002BCD9A0BCA6C0FFE2CC2B20EE32BCD9A0BC2FF0ED13A7D31E3 +:180F7800B7EBC2C00BD5B621870FC4E014E1C93F45787472612069675B +:180F90006E6F7265640D0A00CD310DB7C2B10F237E23B61E06CA560711 +:180FA800235E2356EB227931EBCD9A0BFE83C2980FC32B0F110000C467 +:180FC000FD11227E31CDEB06C24807F9D57E23F5D5CD271AE3E5CD94FB +:180FD80017E1CD411AE1CD381AE5CD641AE1C190CD381ACAF70FEB2283 +:180FF0000C316960C3560BF92A7E317EFE2CC25A0BCD9A0BCDBF0FCD44 +:181008001910F6373A5D318FB7E8C35407CD100A282B1600D50E01CD65 +:181020001F07CD90102280312A8031C178FE78D40A107E1600D6B3DAE3 +:181038005110FE03D25110FE0117AABA57DA4207227531CD9A0BC335E5 +:18105000107AB7C278117E227531D6ACD8FE07D05F3A5D313DB37BCA2B +:18106800D61507835F213406197856BAD023CD0A10C5012810C5434A76 +:18108000CD1A1A58514E234623C52A7531C31C10AF325D31CD9A0B1E51 +:1810980024CA5607DAF01ACD380CD2F710FE262012CD9A0BFE48CA341B +:1810B0001FFE42CAA41F1E02CA5607FEACCA9010FE2ECAF01AFEADCA6C +:1810C800E610FE22CA9F14FEAACAD811FEA7CA0314D6B6D20811CD1543 +:1810E00010CD100A29C9167DCD1C102A8031E5CD121ACD0A10E1C9CD6C +:1810F800FD11E5EB2294313A5D31B7CC271AE1C90600074FC5CD9A0B52 +:1811100079FE31DA2F11CD1510CD100A2CCD0B10EB2A9431E3E5EBCDBE +:181128006817EBE3C33711CDDE10E311F210D5019304094E236669E907 +:1811400015FEADC8FE2DC814FE2BC8FEACC82BC9F6AFF5CD0A10CD4C17 +:181158000CF1EBC1E3EBCD2A1AF5CD4C0CF1C17921C113C27311A34F85 +:1811700078A2E9B34F78B2E9218A113A5D311F7A175F166478BAD0C37D +:1811880079108C1179B71FC1D1F5CD0C1021CE11E5CA641AAF325D31CE +:1811A000D5CD23167E23234E2346D1C5F5CD2716CD381AF157E17BB2D7 +:1811B800C87AD601D8AFBB3CD0151D0ABE2303CAB6113FC3F4193C8F2D +:1811D000C1A0C6FF9FC3FB19165ACD1C10CD0A10CD4C0C7B2F4F7A2F54 +:1811E800CDC113C1C328102BCD9A0BC8CD100A2C01EF11C5F6AF325C21 +:181200003146CD380CDA4207AF4F325D31CD9A0BDA1912CD380CDA26E5 +:18121800124FCD9A0BDA1A12CD380CD21A12D624C235123C325D310FC8 +:18123000814FCD9A0B3A7B313DCAE212F245127ED628CABA12AF327BCC +:1812480031E550592A8E31CD0A0A119031CA2A192A8831EB2A8631CDAA +:181260000A0ACA7812799623C26D12789623CAAC1223232323C35F1222 +:18127800E1E3D511FA10CD0A0AD1CAAF12E3E5C50106002A8A31E50906 +:18129000C1E5CD0E07E1228A3160692288312B3600CD0A0AC29E12D1D7 +:1812A80073237223EBE1C932973121DE06229431E1C9E52A5C31E35708 +:1812C000D5C5CD400CC1F1EBE3E5EB3C577EFE2CCAC012CD100A29220A +:1812D8008031E1225C311E00D511E5F52A88313E19EB2A8A31EBCD0A13 +:1812F0000ACA1A137EB923C2FC127EB8235E235623C2E8123A5C31B72E +:18130800C24B07F1444DCA2A1996CA78131E10C35607110400F1CA61C0 +:181320000C712370234FCD1F07232322753171233A5C311779010B003B +:18133800D23D13C10371237023F5E5CDD51AEBE1F13DC23513F5424B74 +:18135000EB19DA3707CD2807228A312B3600CD0A0AC25B1303572A7525 +:18136800315EEB2909EB2B2B73237223F1DA9C13474F7E2316E15E232C +:181380005623E3F5CD0A0AD21513E5CDD51AD119F13D444DC27D132964 +:1813980029C109EB2A8031C92A8A31EB210000393A5D31B7CABC13CDAC +:1813B0002316CD23152A0A31EB2A73317D934F7C9A41501E00215D31F6 +:1813C800730690C3001A3A5B3147AFC3C213CD5914CD4B1401310DC569 +:1813E000D5CD100A28CDFD11E5EB2B562B5EE1CD0A10CD100A29CD10A7 +:1813F8000AB4444DE3712370C39814CD5914D5CDDE10CD0A10E35E2323 +:1814100056237AB3CA4E077E23666FE52A8E31E3228E312A9231E52AFB +:181428009031E5219031D5CD411AE1CD07102BCD9A0BC24207E1229027 +:1814400031E1229231E1228E31E1C9E52A0C31237CB5E1C01E16C356A3 +:1814580007CD100AA73E80327B31B647CD0212C30A10CD0A10CD8E1B33 +:18147000CD9E14CD2316017E16C57E2323E5CDF914E14E2346CD9214F7 +:18148800E56FCD1616D1C9CDF914216F31E5772323732372E1C92B0645 +:1814A0002250E50EFF237E0CB7CAB414BACAB414B8C2A514FE22CC9AD5 +:1814B8000BE323EB79CD9214116F312A61312294313E01325D31CD44D0 +:1814D0001ACD0A0A226131E17EC01E1EC3560723CD9E14CD2316CD382D +:1814E8001A1C1DC80ACD1B0AFE0DCC4C0E03C3EA14B70EF1F52A0A31D0 +:18150000EB2A73312F4F06FF0923CD0A0ADA171522733123EBF1C9F105 +:181518001E1ACA5607BFF501FB14C52A5F31227331210000E52A0A31E8 +:18153000E5216331EB2A6131EBCD0A0A013415C288152A8631EB2A886F +:1815480031EBCD0A0ACA5B157E2323B7CD8B15C34515C1EB2A8A31EBD3 +:18156000CD0A0ACAB115CD381A7BE509B7F25A15227531E14E0600095C +:181578000923EB2A7531EBCD0A0ACA5B15017A15C5F6807E23235E235E +:181590005623F0B7C8444D2A7331CD0A0A6069D8E1E3CD0A0AE3E560AD +:1815A80069D0C1F1F1E5D5C5C9D1E17DB4C82B462B4EE52B2B6E2600A3 +:1815C0000950592B444D2A7331CD1107E171237069602BC32615C5E571 +:1815D8002A9431E3CD9010E3CD0B107EE52A9431E5861E1CDA5607CDF6 +:1815F0008F14D1CD2716E3CD2616E52A7131EBCD0D16CD0D16212510A7 +:18160800E3E5C3C014E1E37E23234E23466F2C2DC80A120313C317167A +:18162000CD0B102A9431EBCD4116EBC0D550591B4E2A7331CD0A0AC2C9 +:181638003F164709227331E1C92A61312B462B4E2B2BCD0A0AC0226165 +:1816500031C901D113C5CD2016AF57325D317EB7C901D113C5CD561634 +:18166800CA610C23235E23561AC93E01CD8F14CD6B172A713173C1C372 +:18168000C014CD1B17AFE34FE57EB8DA901678110E00C5CDF914C1E12B +:18169800E5232346236668060009444DCD92146FCD1616D1CD2716C3BF +:1816B000C014CD1B17D1D51A90C38616EB7ECD20170405CA610CC51E10 +:1816C800FFFE29CAD516CD100A2CCD6817CD100A29F1E3018816C53D4B +:1816E000BE0600D04F7E91BB47D843C9CD5616CA09185F23237E23664A +:1816F8006FE5194672E3C57EFE24C20A17CD341F180DFE25C21417CD68 +:18171000A41F1803CDF01AC1E170C9EBCD100A29C1D1C543C9CD6B1784 +:1817280032EF30CDEE30C3D113CD5517C3B630CD5517F51E002BCD9A06 +:181740000BCA4B17CD100A2CCD6817C1CDEE30ABA0CA4C17C9CD6817C2 +:1817580032EF3032B730CD100A2CC36817CD9A0BCD0710CD460C7AB714 +:18177000C2610C2BCD9A0B7BC9CD4C0C1AC3D113CD0710CD4C0CD5CDC0 +:18178800100A2CCD6817D112C921671CCD381AC3A317CD381A21C1D1F9 +:1817A000CD121A78B7C83A9731B7CA2A1A90D2BD172F3CEBCD1A1AEB02 +:1817B800CD2A1AC1D1FE19D0F5CD4F1A67F1CD6818B4219431F2E31739 +:1817D000CD4818D229182334CA51072E01CD7E18C32918AF90477E9B13 +:1817E8005F237E9A57237E994FDC54186863AF4779B7C216184A546543 +:181800006F78D608FEE0C2F717AF329731C905297A1757798F4FF20E7E +:1818180018785C45B7CA29182197318677D20918C878219731B7FC3BD5 +:181830001846237EE680A94FC32A1A1CC014C00CC00E8034C0C3510723 +:181848007E835F237E8A57237E894FC92198317E2F77AF6F90477D9B49 +:181860005F7D9A577D994FC90600D608DA7718435A510E00C36A18C621 +:18187800096FAF2DC8791F4F7A1F577B1F5F781F47C37A1800000081B8 +:1818900003AA561980F122768045AA3882CDE919B7EA610C2197317EAE +:1818A80001358011F30490F570D5C5CDA317C1D104CD3F19218C18CD07 +:1818C0009A17219018CD311D018080110000CDA317F1CD641B013180F3 +:1818D80011187221C1D1CDE919C82E00CDA7197932A631EB22A73101F0 +:1818F0000000505821F417E5210019E5E52194317E23B7CA2C19E52EC3 +:18190800081F6779D21A19E52AA73119EBE13AA631891F4F7A1F577B7C +:181920001F5F781F472D7CC20919E1C9435A514FC9CD1A1A012084115F +:181938000000CD2A1AC1D1CDE919CA45072EFFCDA71934342B7E32C250 +:18195000302B7E32BE302B7E32BA3041EBAF4F575F32C530E5C57DCDC6 +:18196800B930DE003FD2771932C530F1F137D2C1E1793C3D1FFA2A18FE +:18198000177B175F7A175779174F297817473AC5301732C53079B2B336 +:18199800C26419E521973135E1C26419C3510778B7CACB197D21973177 +:1819B000AE80471FA878F2CA19C68077CA2A19CD4F1A772BC9CDE9195C +:1819C8002FE1B7E1F20918C35107CD351A78B7C8C602DA510747CDA36D +:1819E0001721973134C0C351073A9731B7C83A9631FE2F179FC03CC9B6 +:1819F800CDE91906881100002197314F70060023368017C3F117CDE94A +:181A100019F02196317EEE8077C9EB2A9431E3E52A9631E3E5EBC9CDC5 +:181A2800381AEB2294316069229631EBC92194315E2356234E23462362 +:181A4000C911943106041A77132305C2461AC92196317E07371F773FB5 +:181A58001F2323777907371F4F1FAEC978B7CAE91921F219E5CDE91904 +:181A700079C8219631AE79F8CD7E1A1FA9C92378BEC02B79BEC02B7A40 +:181A8800BEC02B7B96C0E1E1C9474F575FB7C8E5CD351ACD4F1AAE672A +:181AA000FCB51A3E9890CD68187C17DC3B180600DC5418E1C91B7AA3BE +:181AB8003CC00BC92197317EFE983A9431D07ECD911A36987BF57917B6 +:181AD000CDF117F1C921000078B1C83E1029DA1513EB29EBD2EB1A0905 +:181AE800DA15133DC2DD1AC9FE2DF5CAFC1AFE2BCAFC1A2BCD091847BC +:181B0000575F2F4FCD9A0BDA4D1BFE2ECA281BFE45C22C1BCD9A0BCD21 +:181B18004011CD9A0BDA6F1B14C22C1BAF935F0C0CCA041BE57B90F4EB +:181B3000451BF23B1BF5CD3119F13CC22F1BD1F1CC121AEBC9C8F5CDB8 +:181B4800D219F13DC9D557788947C5E5D5CDD219F1D630CD641BE1C113 +:181B6000D1C3041BCD1A1ACDFB19C1D1C3A3177B0707830786D6305FCB +:181B7800C31A1BE521DA06CDE014E1EBAF0698CD001A21DF14E5219903 +:181B900031E5CDE9193620F29C1B362D233630CA521CE5FC121AAFF584 +:181BA800CD581C01439111F84FCD641AB7E2C91BF1CD461BF5C3AB1B52 +:181BC000CD3119F13CF5CD581CCD91173CCD911ACD2A1A010603F181DD +:181BD8003CFAE51BFE08D2E51B3C473E023D3DE1F5116B1C05C2F61B64 +:181BF000362E2336302305362ECC3F1AC5E5D5CD351AE1062F047B9679 +:181C08005F237A9E5723799E4F2B2BD2051CCD481823CD2A1AEBE17064 +:181C200023C10DC2F61B05CA361C2B7EFE30CA2A1CFE2EC43F1AF1CADC +:181C3800551C364523362BF2461C362D2F3C062F04D60AD2481CC63AB3 +:181C5000237023772371E1C901749411F723CD641AB7E1E2C01BE90054 +:181C6800000080A08601102700E803006400000A000001000021121ADF +:181C8000E3E9CD1A1A21671CCD271AC1D1CDE91978CAD01CF29B1CB7DE +:181C9800CA4507B7CA0A18D5C579F67FCD351AF2B81CD5C5CDBC1AC113 +:181CB000D1F5CD641AE17C1FE1229631E1229431DC7D1CCC121AD5C5F6 +:181CC800CD9D18C1D1CDDE18CD1A1A013881113BAACDDE183A9731FEB9 +:181CE00088D2C519CDBC1AC680C602DAC519F5218C18CD9417CDD5185F +:181CF800F1C1D1F5CDA017CD121A21101DCD401D110000C14AC3DE1892 +:181D100008402E9474704F2E776E02887AE6A02A7C50AAAA7EFFFF7F9C +:181D28007F0000808100000081CD1A1A11DC18D5E5CD351ACDDE18E122 +:181D4000CD1A1A7E23CD271A06F1C1D13DC8D5C5F5E5CDDE18E1CD3830 +:181D58001AE5CDA317E1C3491DCDE91921C930FAC21D21EA30CD271AD8 +:181D700021C930C886E6070600772387874F09CD381ACDDE183AC830EC +:181D88003CE6030600FE018832C83021C61D87874F09CD9417CD351A69 +:181DA0007B59EE4F4F36802B46368021C730347ED6ABC2B91D770C1573 +:181DB8001CCDF41721EA30C3411A772B772B77C39D1D68B1466899E945 +:181DD000926910D1756821201ECD9417CD1A1A01498311DB0FCD2A1A91 +:181DE800C1D1CD3F19CD1A1ACDBC1AC1D1CDA01721241ECD9A17CDE9DB +:181E00001937F20C1ECD9117CDE919B7F5F4121A21241ECD9417F1D4AE +:181E1800121A21281EC3311DDB0F49810000007F05BAD71E866426997E +:181E30008758342387E05DA586DA0F4983CD1A1ACDDC1DC1E1CD1A1A56 +:181E4800EBCD2A1ACDD61DC33D19CDE919FC7D1CFC121A3A9731FE81A0 +:181E6000DA6F1E0100815159CD3F19219A17E521791ECD311D21201EC9 +:181E7800C9094AD73B78026E847BFEC12F7C74319A7D843D5A7DC87F38 +:181E9000917EE4BB4C7E6CAAAA7F00000081C9D7C93E0CC3DB1FCD685D +:181EA800177B32F230C9CD0710CD4C0CED53F630ED53F830C9CD4C0CAE +:181EC000D5E146237EC3C213CD0710CD4C0CD5CD100A2CCD0710CD4CE7 +:181ED8000CE3732372E1C9CD0A10CD4C0CC52199317AFE00280CCD1705 +:181EF0001F78FE302802702371237BCD171F7AFE00200578FE302802D9 +:181F080070237123AF772377C1219931C3701447E60FFE0A3802C6079C +:181F2000C6304F780F0F0F0FE60FFE0A3802C607C63047C9EB2100009A +:181F3800CD4D1FDA6D1F1805CD4D1F381F29292929B56F18F3131AFE47 +:181F500020CA4D1FD630D8FE0A3805D607FE0AD8FE103FC9EB7A4BE598 +:181F6800CDC113E1C91E26C35607CD0A10CD4C0CC52199310611057862 +:181F8000FE012808CB13CB1230F41804CB13CB123E30CE007723052069 +:181F9800F3AF772377C1219931C37014EB210000CDC11FDACF1FD63004 +:181FB00029B56FCDC11F30F6EB7A4BE5CDC113E1C9131AFE20CAC11F24 +:181FC800FE30D8FE323FC91E28C35607DD21FFFFC3A603C30800C30067 +:181FE000003E0032FD30C3AD03ED45F5A0C1B83E00C9CD1B0AC3420E8D +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/CBIOS128.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/CBIOS128.HEX new file mode 100644 index 0000000..9d70d90 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/CBIOS128.HEX @@ -0,0 +1,59 @@ +:18E60000C351E7C3B5E7C31DE8C358E8C396E8C37EE8C38AE8C34CE847 +:18E61800C3EFE8C3C7E8C3FBE8C300E9C305E9C30DE9C321E9C3C4E8E6 +:18E63000C30AE900000000000000000DEB33E700008DEB000000000092 +:18E648000000000DEB42E700008EEC00000000000000000DEB42E700FE +:18E66000008FED00000000000000000DEB42E7000090EE000000000087 +:18E678000000000DEB42E7000091EF00000000000000000DEB42E700C8 +:18E690000092F000000000000000000DEB42E7000093F100000000004B +:18E6A8000000000DEB42E7000094F200000000000000000DEB42E70092 +:18E6C0000095F300000000000000000DEB42E7000096F400000000000F +:18E6D8000000000DEB42E7000097F500000000000000000DEB42E7005C +:18E6F0000098F600000000000000000DEB42E7000099F70000000000D3 +:18E708000000000DEB42E700009AF800000000000000000DEB42E70025 +:18E72000009BF900000000000000000DEB42E700009CFA8000051F01F1 +:18E73800FB07FF01F000000001008000051F01FF07FF01F0000000003B +:18E7500000F331C1FB3E01D3383E95D380D382CDF9EA0C43502F4D2021 +:18E7680042494F5320322E3020627920472E20536561726C652032302E +:18E7800031330D0A0D0A43502F4D20322E322028632920313937392040 +:18E798006279204469676974616C2052657365617263680D0A00AF326B +:18E7B0000400C3F5E7F331C1FB060B3E0032C9FBD38CD38B2100D0DB00 +:18E7C80089FE8020FA3AC9FBD38A3E00D389C50E040680DB89FEE02064 +:18E7E000FADB8877230520F30D20EE3AC9FB3C32C9FBC110D2AF32CB78 +:18E7F800FB32CDFB21800022D6FB3EC33200002103E6220100320500E9 +:18E810002106D82206003A04004FC300D03A0300E60BFE0A280AFE0241 +:18E828002814E603FE01200EDB80E601FE0028033EFFC93E00C9DB82B1 +:18E84000E601FE0028033EFFC93E00C9F53A0300E608FE08201C180E1B +:18E85800F53A0300E603FE0228EBFE01200CF1DB80E601FE0028F8DB23 +:18E8700081C9F1DB82E601FE0028F8DB83C9F53A0300E6C0FE40202670 +:18E88800181AF53A0300E620FE20201A180EF53A0300E603FE0228DF6E +:18E8A000FE01200ACDB8E828FB79D381F1C9CDBEE828FB79D383F1C901 +:18E8B800DB800FCB47C9DB820FCB47C93EFFC921000079FE10380D3A8F +:18E8D0000400B9C0AF32040032C1FBC932C1FBCB07CB07CB07CB0721C0 +:18E8E80033E606004F09C93ACCFBB7200332CBFB010000ED43C2FBC94E +:18E90000ED43C4FBC9ED43D6FBC9C5E1C9AF32CDFB3E0132D4FB32D320 +:18E91800FB3E0232D5FBC388E9AF32D4FB7932D5FBFE0220173E203284 +:18E93000CDFB3AC1FB32CEFB2AC2FB22CFFB3AC4FB32D1FB3ACDFBB793 +:18E9480028363D32CDFB3AC1FB21CEFBBEC280E921CFFBCD1FEAC28056 +:18E96000E93AC4FB21D1FBBEC280E9347EFE80380936002ACFFB232207 +:18E97800CFFBAF32D3FB1808AF32CDFB3C32D3FBAF32D2FB3AC4FBB7AB +:18E990001FB71F32CAFB21CBFB7E3601B728213AC1FB21C6FBBE201120 +:18E9A80021C7FBCD1FEA20093ACAFB21C9FBBE28243ACCFBB7C4C4EA5D +:18E9C0003AC1FB32C6FB2AC2FB22C7FB3ACAFB32C9FB3AD3FBB7C4957E +:18E9D800EAAF32CCFB3AC4FBE6036F26002929292929292911D8FB1902 +:18E9F000EB2AD6FB0E803AD4FBB720063E0132CCFBEB1A1377230D209E +:18EA0800F93AD5FBFE013AD2FBC0B7C0AF32CCFBCDC4EA3AD2FBC9EBD8 +:18EA200021C2FB1ABEC013231ABEC92AC7FBCB05CB05CB05CB05CB0595 +:18EA38007DE6E06F3AC9FB85329DFB2AC7FBCB0DCB0DCB0D7DE61F6F62 +:18EA5000CB04CB04CB04CB04CB047CE620673AC6FBCB07CB07CB07CB7E +:18EA680007CB07CB07E6C08485329EFB3AC6FBCB0FCB0FE603329FFB0D +:18EA80003E0032A0FB3A9FFBD38C3A9EFBD38B3A9DFBD38AC9F5C5E578 +:18EA9800DB89FE8020FACD2BEA3E00D3890E0421D8FB0680DB89FEE020 +:18EAB00020FADB8877230520F30D20EEE1C1F1AF32D2FBC9F5C5E5DB80 +:18EAC80089FE8020FACD2BEA3E01D3890E0421D8FB0680DB89FEA020EA +:18EAE000FAC5066410FEC17ED388230520ED0D20E8E1C1F1AF32D2FBC2 +:15EAF800C9E3F5C57EFE0028074FCD96E82318F423C1F1E3C9AE +:04FB9D000000000064 +:15FDD8003E01D338F1FE0128043E0118023E00320300C300E63B +:12FFE8003E01D33821004111000101008FEDB0C300E673 +:02FFFE00D8FD2C +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/CH376S.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/CH376S.HEX new file mode 100644 index 0000000..0fe183e --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/CH376S.HEX @@ -0,0 +1,6 @@ +:18400000CD6440436865636B2043483337367320636F6D6D756E696320 +:184018006174696F6E0D0A00CD644053656E6420410D0A003E05D320B5 +:184030003E06D3203E41D320AFD320DB20EEFFCD644052656365697676 +:1840480065642000CFCD64400D0A00C9063978FE2FC8D320AFDB20CF3F +:154060000518F3C9E3F5C57EFE002804CF2318F723C1F1E3C9AB +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/CPM22.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/CPM22.HEX new file mode 100644 index 0000000..773954a --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/CPM22.HEX @@ -0,0 +1,238 @@ +:18D00000C35CD3C358D37F00436F7079726967687420313937392028BE +:18D018006329206279204469676974616C20526573656172636820200E +:18D0300020202020000000000000000000000000000000000000000068 +:18D04800000000000000000000000000000000000000000000000000D0 +:18D06000000000000000000000000000000000000000000000000000B8 +:18D078000000000000000000000000000000000008D000005F0E02C396 +:18D090000500C5CD8CD0C1C93E0DCD92D03E0AC392D03E20C392D0C5DC +:18D0A800CD98D0E17EB7C823E5CD8CD0E1C3ACD00E0DC305005F0E0EAE +:18D0C000C30500CD050032EED73CC90E0FC3C3D0AF32EDD711CDD7C332 +:18D0D800CBD00E10C3C3D00E11C3C3D00E12C3C3D011CDD7C3DFD00E11 +:18D0F00013C30500CD0500B7C90E14C3F4D011CDD7C3F9D00E15C3F437 +:18D10800D00E16C3C3D00E17C305001EFF0E20C30500CD13D18787877F +:18D120008721EFD7B6320400C93AEFD7320400C9FE61D8FE7BD0E65F10 +:18D13800C93AABD7B7CA96D13AEFD7B73E00C4BDD011ACD7CDCBD0CA66 +:18D1500096D13ABBD73D32CCD711ACD7CDF9D0C296D11107D0218000A6 +:18D168000680CD42D421BAD73600233511ACD7CDDAD0CA96D13AEFD7CA +:18D18000B7C4BDD02108D0CDACD0CDC2D1CAA7D1CDDDD1C382D3CDDD6E +:18D19800D1CD1AD10E0A1106D0CD0500CD29D12107D0462378B7CABA4A +:18D1B000D17ECD30D17705C3ABD1772108D02288D0C90E0BCD0500B73A +:18D1C800C80E01CD0500B7C90E19C305001180000E1AC3050021ABD713 +:18D1E0007EB7C83600AFCDBDD011ACD7CDEFD03AEFD7C3BDD01128D37F +:18D1F8002100D806061ABEC2CFD3132305C2FDD1C9CD98D02A8AD07E13 +:18D21000FE20CA22D2B7CA22D2E5CD8CD0E123C30FD23E3FCD8CD0CD8C +:18D2280098D0CDDDD1C382D31AB7C8FE20DA09D2C8FE3DC8FE5FC8FE99 +:18D240002EC8FE3AC8FE3BC8FE3CC8FE3EC8C91AB7C8FE20C013C34F74 +:18D25800D2856FD024C93E0021CDD7CD59D2E5E5AF32F0D72A88D0EB61 +:18D27000CD4FD2EB228AD0EBE11AB7CA89D2DE4047131AFE3ACA90D299 +:18D288001B3AEFD777C396D27832F0D770130608CD30D2CAB9D223FE8A +:18D2A0002AC2A9D2363FC3ABD2771305C298D2CD30D2CAC0D213C3AFEF +:18D2B800D223362005C2B9D20603FE2EC2E9D213CD30D2CAE9D223FE87 +:18D2D0002AC2D9D2363FC3DBD2771305C2C8D2CD30D2CAF0D213C3DFCF +:18D2E800D223362005C2E9D2060323360005C2F2D2EB2288D0E1010B22 +:18D3000000237EFE3FC209D3040DC201D378B7C9444952204552412003 +:18D31800545950455341564552454E2055534552001600000000211001 +:18D33000D30E0079FE06D011CED706041ABEC24FD3132305C23CD31A15 +:18D34800FE20C254D379C92305C24FD30CC333D3AF3207D031ABD7C573 +:18D36000791F1F1F1FE60F5FCD15D1CDB8D032ABD7C179E60F32EFD789 +:18D37800CDBDD03A07D0B7C298D331ABD7CD98D0CDD0D1C641CD8CD0C8 +:18D390003E3ECD8CD0CD39D1118000CDD8D1CDD0D132EFD7CD5ED2C4DB +:18D3A80009D23AF0D7B7C2A5D6CD2ED321C1D35F160019197E23666FFD +:18D3C000E977D41FD55DD5ADD510D68ED6A5D621F3762200D02100D047 +:18D3D800E901DFD3C3A7D052656164206572726F720001F0D3C3A7D0A3 +:18D3F0004E6F2066696C6500CD5ED23AF0D7B7C209D221CED7010B0084 +:18D408007EFE20CA33D423D630FE0AD209D25778E6E0C209D278070709 +:18D420000780DA09D280DA09D282DA09D2470DC208D4C97EFE20C2092A +:18D43800D2230DC233D478C906037E12231305C242D4C921800081CD6C +:18D4500059D27EC9AF32CDD73AF0D7B7C83D21EFD7BEC8C3BDD03AF029 +:18D46800D7B7C83D21EFD7BEC83AEFD7C3BDD0CD5ED2CD54D421CED7A4 +:18D480007EFE20C28FD4060B363F2305C288D41E00D5CDE9D0CCEAD305 +:18D49800CA1BD53AEED70F0F0FE6604F3E0ACD4BD417DA0FD5D17B1C90 +:18D4B000D5E603F5C2CCD4CD98D0C5CDD0D1C1C641CD92D03E3ACD9219 +:18D4C800D0C3D4D4CDA2D03E3ACD92D0CDA2D0060178CD4BD4E67FFE1E +:18D4E00020C2F9D4F1F5FE03C2F7D43E09CD4BD4E67FFE20CA0ED53E70 +:18D4F80020CD92D00478FE0CD20ED5FE09C2D9D4CDA2D0C3D9D4F1CDAF +:18D51000C2D1C21BD5CDE4D0C398D4D1C386D7CD5ED2FE0BC242D5013D +:18D5280052D5CDA7D0CD39D12107D035C282D3237EFE59C282D3232211 +:18D5400088D0CD54D411CDD7CDEFD03CCCEAD3C386D7416C6C20287986 +:18D558002F6E293F00CD5ED2C209D2CD54D4CDD0D0CAA7D5CD98D0211E +:18D57000F1D736FF21F1D77EFE80DA87D5E5CDFED0E1C2A0D5AF773499 +:18D58800218000CD59D27EFE1ACA86D7CD8CD0CDC2D1C286D7C374D581 +:18D5A0003DCA86D7CDD9D3CD66D4C309D2CDF8D3F5CD5ED2C209D2CDFD +:18D5B80054D411CDD7D5CDEFD0D1CD09D1CAFBD5AF32EDD7F16F2600E0 +:18D5D000291100017CB5CAF1D52BE521800019E5CDD8D111CDD7CD049C +:18D5E800D1D1E1C2FBD5C3D4D511CDD7CDDAD03CC201D60107D6CDA757 +:18D60000D0CDD5D1C386D74E6F20737061636500CD5ED2C209D23AF002 +:18D61800D7F5CD54D4CDE9D0C279D621CDD711DDD70610CD42D42A886D +:18D63000D0EBCD4FD2FE3DCA3FD6FE5FC273D6EB232288D0CD5ED2C270 +:18D6480073D6F14721F0D77EB7CA59D6B870C273D670AF32CDD7CDE955 +:18D66000D0CA6DD611CDD7CD0ED1C386D7CDEAD3C386D7CD66D4C309D7 +:18D67800D20182D6CDA7D0C386D746696C652065786973747300CDF806 +:18D69000D3FE10D209D25F3ACED7FE20CA09D2CD15D1C389D7CDF5D18A +:18D6A8003ACED7FE20C2C4D63AF0D7B7CA89D73D32EFD7CD29D1CDBDA9 +:18D6C000D0C389D711D6D71AFE20C209D2D5CD54D4D12183D7CD40D4D5 +:18D6D800CDD0D0CA6BD7210001E5EBCDD8D111CDD7CDF9D0C201D7E193 +:18D6F000118000191100D07D937C9AD271D7C3E1D6E13DC271D7CD6682 +:18D70800D4CD5ED221F0D7E57E32CDD73E10CD60D2E17E32DDD7AF32A4 +:18D72000EDD7115C0021CDD70621CD42D42108D07EB7CA3ED7FE20CAFC +:18D738003ED723C330D706001181007E12B7CA4FD7042313C343D77879 +:18D75000328000CD98D0CDD5D1CD1AD1CD000131ABD7CD29D1CDBDD00D +:18D76800C382D3CD66D4C309D2017AD7CDA7D0C386D7426164206C6F34 +:18D78000616400434F4DCD66D4CD5ED23ACED7D62021F0D7B6C209D2D9 +:18D79800C382D3000000000000000000000000000000000000242424F5 +:18D7B000202020202053554200000000000000000000000000000000D7 +:18D7C800000000000000202020202020202020202000000000002020A9 +:18D7E00020202020202020202000000000000000000000000000000011 +:18D7F8000000000000000000001600000000C311D899D8A5D8ABD8B135 +:18D81000D8EB2243DBEB7B32D6E52100002245DB39220FDB3141DBAF06 +:18D8280032E0E532DEE52174E5E579FE29D04B2147D85F160019195E9D +:18D8400023562A43DBEBE903E6C8DA90D9CEDA12E60FE6D4DAEDDAF34A +:18D85800DAF8DAE1D9FEDA7EE483E445E49CE4A5E4ABE4C8E4D7E4E0A9 +:18D87000E4E6E4ECE4F5E4FEE404E50AE511E52CDD17E51DE526E52D5A +:18D88800E541E547E54DE50EE453E504DB04DB9BE521CAD8CDE5D8FE6C +:18D8A00003CA0000C921D5D8C3B4D821E1D8C3B4D821DCD8CDE5D8C372 +:18D8B800000042646F7320457272204F6E20203A20244261642053650D +:18D8D00063746F722453656C6563742446696C6520522F4F24E5CDC9D1 +:18D8E800D93A42DBC64132C6D801BAD8CDD3D9C1CDD3D9210EDB7E361D +:18D9000000B7C0C309E6CDFBD8CD14D9D8F54FCD90D9F1C9FE0DC8FEAF +:18D918000AC8FE09C8FE08C8FE20C93A0EDBB7C245D9CD06E6E601C87F +:18D93000CD09E6FE13C242D9CD09E6FE03CA0000AFC9320EDB3E01C913 +:18D948003A0ADBB7C262D9C5CD23D9C1C5CD0CE6C1C53A0DDBB7C40FEF +:18D96000E6C179210CDBFE7FC834FE20D0357EB7C879FE08C279D93526 +:18D97800C9FE0AC03600C979CD14D9D290D9F50E5ECD48D9F1F6404FD4 +:18D9900079FE09C248D90E20CD48D93A0CDBE607C296D9C9CDACD90E98 +:18D9A80020CD0CE60E08C30CE60E23CD48D9CDC9D93A0CDB210BDBBE49 +:18D9C000D00E20CD48D9C3B9D90E0DCD48D90E0AC348D90AFE24C80312 +:18D9D800C54FCD90D9C1C3D3D93A0CDB320BDB2A43DB4E23E50600C51B +:18D9F000E5CDFBD8E67FE1C1FE0DCAC1DAFE0ACAC1DAFE08C216DA7886 +:18DA0800B7CAEFD9053A0CDB320ADBC370DAFE7FC226DA78B7CAEFD973 +:18DA20007E052BC3A9DAFE05C237DAC5E5CDC9D9AF320BDBC3F1D9FEB9 +:18DA380010C248DAE5210DDB3E019677E1C3EFD9FE18C25FDAE13A0B05 +:18DA5000DB210CDBBED2E1D935CDA4D9C34EDAFE15C26BDACDB1D9E1D5 +:18DA6800C3E1D9FE12C2A6DAC5CDB1D9C1E1E5C578B7CA8ADA234E059C +:18DA8000C5E5CD7FD9E1C1C378DAE53A0ADBB7CAF1D9210CDB96320ADF +:18DA9800DBCDA4D9210ADB35C299DAC3F1D9237704C5E54FCD7FD9E1B7 +:18DAB000C17EFE0378C2BDDAFE01CA0000B9DAEFD9E1700E0DC348D9D9 +:18DAC800CD06D9C301DBCD15E6C301DB793CCAE0DA3CCA06E6C30CE6B4 +:18DAE000CD06E6B7CA91E5CD09E6C301DB3A0300C301DB21030071C9E9 +:18DAF800EB4D44C3D3D9CD23D93245DBC93E01C301DB00020000000067 +:18DB1000000000000000000000000000000000000000000000000000FD +:18DB2800000000000000000000000000000000000000000000000000E5 +:18DB400000000000000000210BD85E2356EBE90C0DC81A771323C35063 +:18DB5800DB3A42DB4FCD1BE67CB5C85E23562322B3E5232322B5E52394 +:18DB70002322B7E52323EB22D0E521B9E50E08CD4FDB2ABBE5EB21C151 +:18DB8800E50E0FCD4FDB2AC6E57C21DDE536FFB7CA9DDB36003EFFB700 +:18DBA000C9CD18E6AF2AB5E57723772AB7E5772377C9CD27E6C3BBDB82 +:18DBB800CD2AE6B7C82109D8C34ADB2AEAE50E02CDEADC22E5E522EC79 +:18DBD000E521E5E54E23462AB7E55E23562AB5E57E23666F7993789AC1 +:18DBE800D2FADBE52AC1E57B955F7A9C57E12BC3E4DBE52AC1E519DAB7 +:18DC00000FDC7995789CDA0FDCEBE123C3FADBE1C5D5E5EB2ACEE51972 +:18DC1800444DCD1EE6D12AB5E5732372D12AB7E5732372C179934F78C2 +:18DC30009A472AD0E5EBCD30E64D44C321E621C3E54E3AE3E5B71F0DF7 +:18DC4800C245DC473E08964F3AE2E50DCA5CDCB717C353DC80C92A43E9 +:18DC6000DB11100019093ADDE5B7CA71DC6E2600C9095E2356EBC9CD06 +:18DC78003EDC4F0600CD5EDC22E5E5C92AE5E57DB4C93AC3E52AE5E5A5 +:18DC9000293DC290DC22E7E53AC4E54F3AE3E5A1B56F22E5E5C92A43DF +:18DCA800DB110C0019C92A43DB110F0019EB21110019C9CDAEDC7E3203 +:18DCC000E3E5EB7E32E1E5CDA6DC3AC5E5A632E2E5C9CDAEDC3AD5E53D +:18DCD800FE02C2DEDCAF4F3AE3E58177EB3AE1E577C90C0DC87CB71F62 +:18DCF000677D1F6FC3EBDC0E802AB9E5AF86230DC2FDDCC90C0DC829F7 +:18DD0800C305DDC53A42DB4F210100CD04DDC179B56F78B467C92AAD92 +:18DD2000E53A42DB4FCDEADC7DE601C921ADE54E2346CD0BDD22ADE5CD +:18DD38002AC8E523EB2AB3E5732372C9CD5EDD110900197E17D0210F8B +:18DD5000D8C34ADBCD1EDDC8210DD8C34ADB2AB9E53AE9E5856FD024C5 +:18DD6800C92A43DB110E00197EC9CD69DD3600C9CD69DDF68077C92A0E +:18DD8000EAE5EB2AB3E57B96237A9EC9CD7FDDD813722B73C97B956F8E +:18DD98007A9C67C90EFF2AECE5EB2ACCE5CD95DDD0C5CDF7DC2ABDE51F +:18DDB000EB2AECE519C10CCAC4DDBEC8CD7FDDD0CD2CDDC977C9CD9C5D +:18DDC800DDCDE0DD0E01CDB8DBC3DADDCDE0DDCDB2DB21B1E5C3E3DDD5 +:18DDE00021B9E54E2346C324E62AB9E5EB2AB1E50E80C34FDB21EAE50A +:18DDF8007E23BEC03CC921FFFF22EAE5C92AC8E5EB2AEAE52322EAE547 +:18DE1000CD95DDD219DEC3FEDD3AEAE5E60306058705C220DE32E9E50B +:18DE2800B7C0C5CDC3DBCDD4DDC1C39EDD79E6073C5F57790F0F0FE6DA +:18DE40001F4F788787878787B14F780F0F0FE61F472ABFE5097E071DD7 +:18DE5800C256DEC9D5CD35DEE6FEC1B10F15C264DE77C9CD5EDD111057 +:18DE70000019C50E11D10DC8D53ADDE5B7CA88DEC5E54E0600C38EDE12 +:18DE88000DC54E2346E579B0CA9DDE2AC6E57D917C98D45CDEE123C1DC +:18DEA000C375DE2AC6E50E03CDEADC23444D2ABFE53600230B78B1C20A +:18DEB800B1DE2ACAE5EB2ABFE5732372CDA1DB2AB3E53603233600CDBF +:18DED000FEDD0EFFCD05DECDF5DDC8CD5EDD3EE5BECAD2DE3A41DBBEC4 +:18DEE800C2F6DE237ED624C2F6DE3D3245DB0E01CD6BDECD8CDDC3D2DC +:18DF0000DE3AD4E5C301DBC5F53AC5E52F4779A04FF1A091E61FC1C96C +:18DF18003EFF32D4E521D8E5712A43DB22D9E5CDFEDDCDA1DB0E00CD86 +:18DF300005DECDF5DDCA94DF2AD9E5EB1AFEE5CA4ADFD5CD7FDDD1D2B6 +:18DF480094DFCD5EDD3AD8E54F060079B7CA83DF1AFE3FCA7CDF78FEAC +:18DF60000DCA7CDFFE0C1ACA73DF96E67FC22DDFC37CDFC54ECD07DF8A +:18DF7800C1C22DDF1323040DC353DF3AEAE5E6033245DB21D4E57E1713 +:18DF9000D0AF77C9CDFEDD3EFFC301DBCD54DD0E0CCD18DFCDF5DDC8F3 +:18DFA800CD44DDCD5EDD36E50E00CD6BDECDC6DDCD2DDFC3A4DF5059F4 +:18DFC00079B0CAD1DF0BD5C5CD35DE1FD2ECDFC1D12AC6E57B957A9CD8 +:18DFD800D2F4DF13C5D5424BCD35DE1FD2ECDFD1C1C3C0DF173CCD643E +:18DFF000DEE1D1C979B0C2C0DF210000C90E001E20D506002A43DB09D4 +:18E00800EBCD5EDDC1CD4FDBCDC3DBC3C6DDCD54DD0E0CCD18DF2A433B +:18E02000DB7E1110001977CDF5DDC8CD44DD0E101E0CCD01E0CD2DDFBA +:18E03800C327E00E0CCD18DFCDF5DDC80E001E0CCD01E0CD2DDFC340FF +:18E05000E00E0FCD18DFCDF5DDC8CDA6DC7EF5E5CD5EDDEB2A43DB0EA0 +:18E0680020D5CD4FDBCD78DDD1210C00194E210F001946E1F17779BE1E +:18E0800078CA8BE03E00DA8BE03E802A43DB110F001977C97E23B62B57 +:18E09800C01A7713231A771B2BC9AF3245DB32EAE532EBE5CD1EDDC0BD +:18E0B000CD69DDE680C00E0FCD18DFCDF5DDC8011000CD5EDD09EB2AA0 +:18E0C80043DB090E103ADDE5B7CAE8E07EB71AC2DBE077B7C2E1E07EBB +:18E0E00012BEC21FE1C3FDE0CD94E0EBCD94E0EB1ABEC21FE113231AB4 +:18E0F800BEC21FE10D13230DC2CDE001ECFF09EB091ABEDA17E17701C6 +:18E11000030009EB097E123EFF32D2E5C310E02145DB35C9CD54DD2A27 +:18E1280043DBE521ACE52243DB0E01CD18DFCDF5DDE12243DBC8EB2183 +:18E140000F00190E11AF77230DC246E1210D001977CD8CDDCDFDDFC3E1 +:18E1580078DDAF32D2E5CDA2E0CDF5DDC82A43DB010C00097E3CE61FEF +:18E1700077CA83E1473AC5E5A021D2E5A6CA8EE1C3ACE10102000934E0 +:18E188007EE60FCAB6E10E0FCD18DFCDF5DDC2ACE13AD3E53CCAB6E14D +:18E1A000CD24E1CDF5DDCAB6E1C3AFE1CD5AE0CDBBDCAFC301DBCD05B7 +:18E1B800DBC378DD3E0132D5E53EFF32D3E5CDBBDC3AE3E521E1E5BEFF +:18E1D000DAE6E1FE80C2FBE1CD5AE1AF32E3E53A45DBB7C2FBE1CD77D6 +:18E1E800DCCD84DCCAFBE1CD8ADCCDD1DBCDB2DBC3D2DCC305DB3E0117 +:18E2000032D5E53E0032D3E5CD54DD2A43DBCD47DDCDBBDC3AE3E5FE57 +:18E2180080D205DBCD77DCCD84DC0E00C26EE2CD3EDC32D7E501000079 +:18E23000B7CA3BE24F0BCD5EDC444DCDBEDF7DB4C248E23E02C301DBE0 +:18E2480022E5E5EB2A43DB011000093ADDE5B73AD7E5CA64E2CD64DDBE +:18E2600073C36CE24F060009097323720E023A45DBB7C0C5CD8ADC3AA0 +:18E27800D5E53D3DC2BBE2C1C5793D3DC2BBE2E52AB9E557772314F27F +:18E290008CE2CDE0DD2AE7E50E0222E5E5C5CDD1DBC1CDB8DB2AE5E539 +:18E2A8000E003AC4E547A5B823C29AE2E122E5E5CDDADDCDD1DBC1C518 +:18E2C000CDB8DBC13AE3E521E1E5BEDAD2E277340E020000210000F51F +:18E2D800CD69DDE67F77F1FE7FC200E33AD5E5FE01C200E3CDD2DCCD4C +:18E2F0005AE12145DB7EB7C2FEE23D32E3E53600C3D2DCAF32D5E5C585 +:18E308002A43DBEB212100197EE67FF57E17237E17E61F4F7E1F1F1F1B +:18E320001FE60F47F1236E2C2D2E06C28BE32120001977210C001979BB +:18E3380096C247E3210E00197896E67FCA7FE3C5D5CDA2E0D1C12E03B8 +:18E350003A45DB3CCA84E3210C001971210E001970CD51E03A45DB3CEB +:18E36800C27FE3C1C52E040CCA84E3CD24E12E053A45DB3CCA84E3C1F7 +:18E38000AFC301DBE5CD69DD36C0E1C17D3245DBC378DD0EFFCD03E300 +:18E39800CCC1E1C90E00CD03E3CC03E2C9EB194E0600210C00197E0FD0 +:18E3B000E680814F3E0088477E0FE60F8047210E00197E87878787F5F2 +:18E3C8008047F5E17DE1B5E601C90E0CCD18DF2A43DB11210019E57215 +:18E3E00023722372CDF5DDCA0CE4CD5EDD110F00CDA5E3E1E55F7996F1 +:18E3F80023789E237B9EDA06E4732B702B71CD2DDFC3E4E3E1C92A43B0 +:18E41000DB112000CDA5E3212100197123702377C92AAFE53A42DB4F6D +:18E42800CDEADCE5EBCD59DBE1CC47DB7D1FD82AAFE54D44CD0BDD220F +:18E44000AFE5C3A3DE3AD6E52142DBBEC877C321E43EFF32DEE52A4355 +:18E45800DB7EE61F3D32D6E5FE1ED275E43A42DB32DFE57E32E0E5E635 +:18E47000E077CD45E43A41DB2A43DBB677C93E22C301DB21000022ADC4 +:18E48800E522AFE5AF3242DB21800022B1E5CDDADDC321E4CD72DDCD55 +:18E4A00051E4C351E0CD51E4C3A2E00E00EB7EFE3FCAC2E4CDA6DC7E03 +:18E4B800FE3FC472DDCD51E40E0FCD18DFC3E9DD2AD9E52243DBCD514A +:18E4D000E4CD2DDFC3E9DDCD51E4CD9CDFC301DFCD51E4C3BCE1CD5181 +:18E4E800E4C3FEE1CD72DDCD51E4C324E1CD51E4CD16E0C301DF2AAF6F +:18E50000E5C329E53A42DBC301DBEB22B1E5C3DADD2ABFE5C329E52A71 +:18E51800ADE5C329E5CD51E4CD3BE0C301DF2ABBE52245DBC93AD6E531 +:18E53000FEFFC23BE53A41DBC301DBE61F3241DBC9CD51E4C393E3CDDB +:18E5480051E4C39CE3CD51E4C3D2E32A43DB7D2F5F7C2F2AAFE5A45713 +:18E560007DA35F2AADE5EB22AFE57DA36F7CA26722ADE5C93ADEE5B782 +:18E57800CA91E52A43DB36003AE0E5B7CA91E5773ADFE532D6E5CD4563 +:18E59000E42A0FDBF92A45DB7D44C9CD51E43E0232D5E50E00CD07E3BB +:18E5A800CC03E2C9E5000000008000000000000000000000000000007C +:18E5C00000000000000000000000000000000000000000000000000043 +:18E5D8000000000000000000000000000000000000000000000000002B +:18E5F00000000000000000000000000000000000C30000C30000C300CA +:18E6080000C30000C30000C30000C30000C30000C30000C30000C300E2 +:13E6200000C30000C30000C30000C30000C30000C3000055 +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/DOWNLOAD.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/DOWNLOAD.HEX new file mode 100644 index 0000000..643dffe --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/DOWNLOAD.HEX @@ -0,0 +1,19 @@ +:180100003E00326D02327102327202327002218000226E02CD3902FEE0 +:1801180055CA2A02FE3A20F40E13115C00CD05000E16115C00CD050075 +:18013000CD3902FE3E286147C5CD3902C14FCD4C02473A710280327194 +:18014800023A72023C327202782A6E027723226E023A6D023C326D0249 +:18016000FE8020320E15115C00CD05003E2ECD45023A70023CFE40208F +:180178000F3270023E0DCD45023E0ACD45023E00327002218000226EEE +:18019000023E00326D0218983A6D02FE00280D0E15115C00CD05003E4A +:1801A8002ECD45020E10115C00CD0500CD390247C5CD3902C14FCD4C5B +:1801C00002473A720290FE00281A3E0DCD45023E0ACD45021191020EF3 +:1801D80009CD0500CD3902CD3902183CCD390247C5CD3902C14FCD4C8B +:1801F00002473A710290FE0028143E0DCD45023E0ACD45021176020EE5 +:1802080009CD050018123E0DCD45023E0ACD45021173020E09CD0500AF +:180220000E201E00CD0500C30000CD3902CD65025F0E20CD0500C31473 +:18023800011EFF0E06CD0500FE0028F5C90E025FCD0500C978D630FE40 +:180250000A3802D607070707074779D630FE0A3802D60780C9D630FE27 +:180268000AD8D607C90000000000004F4B243D3D3D3D3D3D4368656357 +:180280006B73756D204572726F723D3D3D3D3D3D243D3D3D3D3D3D4636 +:17029800696C65204C656E677468204572726F723D3D3D3D3D3D24D7 +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/DOWNLOAD2.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/DOWNLOAD2.HEX new file mode 100644 index 0000000..3db112d --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/DOWNLOAD2.HEX @@ -0,0 +1,15 @@ +:204100003E00326D02327102327202327002218000226E02CD3902FE55CA2A02FE3A20F401 +:204120000E13115C00CD05000E16115C00CD0500CD3902FE3E286147C5CD3902C14FCD4CB2 +:2041400002473A7102803271023A72023C327202782A6E027723226E023A6D023C326D02F0 +:20416000FE8020320E15115C00CD05003E2ECD45023A70023CFE40200F3270023E0DCD4537 +:20418000023E0ACD45023E00327002218000226E023E00326D0218983A6D02FE00280D0E33 +:2041A00015115C00CD05003E2ECD45020E10115C00CD0500CD390247C5CD3902C14FCD4C89 +:2041C00002473A720290FE00281A3E0DCD45023E0ACD45021191020E09CD0500CD3902CDFB +:2041E0003902183CCD390247C5CD3902C14FCD4C02473A710290FE0028143E0DCD45023E8E +:204200000ACD45021176020E09CD050018123E0DCD45023E0ACD45021173020E09CD0500BA +:204220000E201E00CD0500C30000CD3902CD65025F0E20CD0500C314011EFF0E06CD050027 +:20424000FE0028F5C90E025FCD0500C978D630FE0A3802D607070707074779D630FE0A38B1 +:2042600002D60780C9D630FE0AD8D607C90000000000004F4B243D3D3D3D3D3D43686563EB +:204280006B73756D204572726F723D3D3D3D3D3D243D3D3D3D3D3D46696C65204C656E670E +:2042A0007468204572726F723D3D3D3D3D3D24FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77 +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/FORM128.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/FORM128.HEX new file mode 100644 index 0000000..0c20384 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/FORM128.HEX @@ -0,0 +1,16 @@ +:18500000CDD55043502F4D20466F726D617474657220322E3020627918 +:1850180020472E20536561726C6520323031330D0A003E4132E7503A50 +:18503000E750CF3C32E7503E2032E6503AE650D38A3E00D38B3E00D37D +:185048008C3EE0CDA7503AE6503C32E650FE4020E311400021400006D5 +:185060000F3AE750CF3C32E7503E0032E6503AE650D38A7DD38B7CD3A7 +:185078008CCDA7503AE6503C32E650FE2020E7190520D6CDD5500D0A7A +:18509000466F726D617474696E6720636F6D706C6574650D0A00C9F59F +:1850A800C5E5DB89FE8020FA3E01D3890E0421E8500680DB89FEA0209C +:1850C000FAC5066410FEC17ED388230520ED0D20E5E1C1F1C9E3F5C5C7 +:1850D8007EFE002804CF2318F723C1F1E3C90000E520202020202020D1 +:1850F00020202020000000000000000000000000000000000000000028 +:18510800E520202020202020202020200000000000000000000000004A +:185120000000000000000000E520202020202020202020200000000032 +:1851380000000000000000000000000000000000E5202020202020209A +:18515000202020200000000000000000000000000000000000000000C7 +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/hexFiles/PUTSYS.HEX b/Z80 CPM and bootloader (basmon)/hexFiles/PUTSYS.HEX new file mode 100644 index 0000000..d98532d --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/hexFiles/PUTSYS.HEX @@ -0,0 +1,10 @@ +:18500000CDBA5043502F4D2053797374656D205472616E736665722088 +:18501800627920472E20536561726C6520323031322D31330D0A000601 +:18503000183E0032CB5032CC5032CD5032CE502100D022CF50CD8C50FD +:185048001100022ACF501922CF503ACB503C32CB5010EACDBA500D0AD4 +:1850600053797374656D207472616E7366657220636F6D706C657465B5 +:185078000D0A00C93ACD50D38C3ACC50D38B3ACB50D38AC9F5C5E5DBE1 +:1850900089FE8020FACD7C503E01D3890E040680DB89FEA020FAC50634 +:1850A8006410FEC17ED388230520ED0D20E8E1C1F1C9E3F5C57EFE0025 +:1150C0002804CF2318F723C1F1E3C900000000000031 +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/source/BASMON.LST b/Z80 CPM and bootloader (basmon)/source/BASMON.LST new file mode 100644 index 0000000..75b4060 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/BASMON.LST @@ -0,0 +1,5015 @@ +0001 0000 ;================================================================================== +0002 0000 ; The updates to the original BASIC within this file are copyright Grant Searle +0003 0000 ; +0004 0000 ; You have permission to use this for NON COMMERCIAL USE ONLY +0005 0000 ; If you wish to use it elsewhere, please include an acknowledgement to myself. +0006 0000 ; +0007 0000 ; http://searle.hostei.com/grant/index.html +0008 0000 ; +0009 0000 ; eMail: home.micros01@btinternet.com +0010 0000 ; +0011 0000 ; If the above don't work, please perform an Internet search to see if I have +0012 0000 ; updated the web page hosting service. +0013 0000 ; +0014 0000 ;================================================================================== +0015 0000 +0016 0000 +0017 0000 ;================================================================================== +0018 0000 ; Contents of this file are copyright Grant Searle +0019 0000 ; HEX routines from Joel Owens. +0020 0000 ; +0021 0000 ; You have permission to use this for NON COMMERCIAL USE ONLY +0022 0000 ; If you wish to use it elsewhere, please include an acknowledgement to myself. +0023 0000 ; +0024 0000 ; http://searle.hostei.com/grant/index.html +0025 0000 ; +0026 0000 ; eMail: home.micros01@btinternet.com +0027 0000 ; +0028 0000 ; If the above don't work, please perform an Internet search to see if I have +0029 0000 ; updated the web page hosting service. +0030 0000 ; +0031 0000 ;================================================================================== +0032 0000 +0033 0000 ;------------------------------------------------------------------------------ +0034 0000 ; +0035 0000 ; Z80 Monitor Rom +0036 0000 ; +0037 0000 ;------------------------------------------------------------------------------ +0038 0000 ; General Equates +0039 0000 ;------------------------------------------------------------------------------ +0040 0000 +0041 0000 ;CR .EQU 0DH +0042 0000 ;LF .EQU 0AH +0043 0000 ;ESC .EQU 1BH +0044 0000 ;CTRLC .EQU 03H +0045 0000 M_CLS .EQU 0CH +0046 0000 +0047 0000 +0048 0000 loadAddr .EQU 0D000h ; CP/M load address +0049 0000 numSecs .EQU 24 ; Number of 512 sectors to be loaded +0050 0000 +0051 0000 +0052 0000 RTS_HIGH .EQU 0D5H +0053 0000 RTS_LOW .EQU 095H +0054 0000 +0055 0000 ACIA0_D .EQU $81 +0056 0000 ACIA0_C .EQU $80 +0057 0000 ACIA1_D .EQU $83 +0058 0000 ACIA1_C .EQU $82 +0059 0000 +0060 0000 SD_DATA .EQU 088H +0061 0000 SD_CONTROL .EQU 089H +0062 0000 SD_STATUS .EQU 089H +0063 0000 SD_LBA0 .EQU 08AH +0064 0000 SD_LBA1 .EQU 08BH +0065 0000 SD_LBA2 .EQU 08CH +0066 0000 +0067 3000 .ORG $3000 +0068 3000 +0069 3000 primaryIO .ds 1 +0070 3001 secNo .ds 1 +0071 3002 dmaAddr .ds 2 +0072 3004 +0073 3004 00 lba0 .DB 00h +0074 3005 00 lba1 .DB 00h +0075 3006 00 lba2 .DB 00h +0076 3007 00 lba3 .DB 00h +0077 3008 +0078 3008 stackSpace .ds 32 +0079 3028 M_STACK .EQU $ ; Stack top +0080 3028 +0081 3028 +0082 3028 ;------------------------------------------------------------------------------ +0083 3028 ; START OF MONITOR ROM +0084 3028 ;------------------------------------------------------------------------------ +0085 3028 +0086 0000 MON .ORG $0000 ; MONITOR ROM RESET VECTOR +0087 0000 ;------------------------------------------------------------------------------ +0088 0000 ; Reset +0089 0000 ;------------------------------------------------------------------------------ +0090 0000 F3 RST00 DI ;Disable INTerrupts +0091 0001 C3 94 00 JP M_INIT ;Initialize Hardware and go +0092 0004 00 NOP +0093 0005 00 NOP +0094 0006 00 NOP +0095 0007 00 NOP +0096 0008 ;------------------------------------------------------------------------------ +0097 0008 ; TX a character over RS232 wait for TXDONE first. +0098 0008 ;------------------------------------------------------------------------------ +0099 0008 C3 32 00 RST08 JP conout +0100 000B 00 NOP +0101 000C 00 NOP +0102 000D 00 NOP +0103 000E 00 NOP +0104 000F 00 NOP +0105 0010 ;------------------------------------------------------------------------------ +0106 0010 ; RX a character from buffer wait until char ready. +0107 0010 ;------------------------------------------------------------------------------ +0108 0010 C3 1B 00 RST10 JP conin +0109 0013 00 NOP +0110 0014 00 NOP +0111 0015 00 NOP +0112 0016 00 NOP +0113 0017 00 NOP +0114 0018 ;------------------------------------------------------------------------------ +0115 0018 ; Check input buffer status +0116 0018 ;------------------------------------------------------------------------------ +0117 0018 C3 5C 00 RST18 JP CKINCHAR +0118 001B +0119 001B +0120 001B ;------------------------------------------------------------------------------ +0121 001B ; Console input routine +0122 001B ; Use the "primaryIO" flag to determine which input port to monitor. +0123 001B ;------------------------------------------------------------------------------ +0124 001B conin: +0125 001B 3A 00 30 LD A,(primaryIO) +0126 001E FE 00 CP 0 +0127 0020 20 08 JR NZ,coninB +0128 0022 coninA: +0129 0022 +0130 0022 waitForCharA: +0131 0022 CD 63 00 call ckincharA +0132 0025 28 FB JR Z, waitForCharA +0133 0027 DB 81 IN A,(ACIA0_D) +0134 0029 C9 RET ; Char ready in A +0135 002A +0136 002A coninB: +0137 002A +0138 002A waitForCharB: +0139 002A CD 6A 00 call ckincharB +0140 002D 28 FB JR Z, waitForCharB +0141 002F DB 83 IN A,(ACIA1_D) +0142 0031 C9 RET ; Char ready in A +0143 0032 +0144 0032 ;------------------------------------------------------------------------------ +0145 0032 ; Console output routine +0146 0032 ; Use the "primaryIO" flag to determine which output port to send a character. +0147 0032 ;------------------------------------------------------------------------------ +0148 0032 F5 conout: PUSH AF ; Store character +0149 0033 3A 00 30 LD A,(primaryIO) +0150 0036 FE 00 CP 0 +0151 0038 20 0D JR NZ,conoutB1 +0152 003A 18 01 JR conoutA1 +0153 003C conoutA: +0154 003C F5 PUSH AF +0155 003D +0156 003D CD 50 00 conoutA1: CALL CKACIA0 ; See if ACIA channel A is finished transmitting +0157 0040 28 FB JR Z,conoutA1 ; Loop until ACIA flag signals ready +0158 0042 F1 POP AF ; RETrieve character +0159 0043 D3 81 OUT (ACIA0_D),A ; OUTput the character +0160 0045 C9 RET +0161 0046 +0162 0046 conoutB: +0163 0046 F5 PUSH AF +0164 0047 +0165 0047 CD 56 00 conoutB1: CALL CKACIA1 ; See if ACIA channel B is finished transmitting +0166 004A 28 FB JR Z,conoutB1 ; Loop until ACIA flag signals ready +0167 004C F1 POP AF ; RETrieve character +0168 004D D3 83 OUT (ACIA1_D),A ; OUTput the character +0169 004F C9 RET +0170 0050 +0171 0050 ;------------------------------------------------------------------------------ +0172 0050 ; I/O status check routine +0173 0050 ; Use the "primaryIO" flag to determine which port to check. +0174 0050 ;------------------------------------------------------------------------------ +0175 0050 CKACIA0 +0176 0050 DB 80 IN A,(ACIA0_C) ; Status byte D1=TX Buff Empty, D0=RX char ready +0177 0052 0F RRCA ; Rotates RX status into Carry Flag, +0178 0053 CB 47 BIT 0,A ; Set Zero flag if still transmitting character +0179 0055 C9 RET +0180 0056 +0181 0056 CKACIA1 +0182 0056 DB 82 IN A,(ACIA1_C) ; Status byte D1=TX Buff Empty, D0=RX char ready +0183 0058 0F RRCA ; Rotates RX status into Carry Flag, +0184 0059 CB 47 BIT 0,A ; Set Zero flag if still transmitting character +0185 005B C9 RET +0186 005C +0187 005C ;------------------------------------------------------------------------------ +0188 005C ; Check if there is a character in the input buffer +0189 005C ; Use the "primaryIO" flag to determine which port to check. +0190 005C ;------------------------------------------------------------------------------ +0191 005C CKINCHAR +0192 005C 3A 00 30 LD A,(primaryIO) +0193 005F FE 00 CP 0 +0194 0061 20 07 JR NZ,ckincharB +0195 0063 +0196 0063 ckincharA: +0197 0063 +0198 0063 DB 80 IN A,(ACIA0_C) ; Status byte +0199 0065 E6 01 AND $01 +0200 0067 FE 00 CP $0 ; Z flag set if no char +0201 0069 C9 RET +0202 006A +0203 006A ckincharB: +0204 006A +0205 006A DB 82 IN A,(ACIA1_C) ; Status byte +0206 006C E6 01 AND $01 +0207 006E FE 00 CP $0 ; Z flag set if no char +0208 0070 C9 RET +0209 0071 +0210 0071 ;------------------------------------------------------------------------------ +0211 0071 ; Filtered Character I/O +0212 0071 ;------------------------------------------------------------------------------ +0213 0071 +0214 0071 D7 RDCHR RST 10H +0215 0072 FE 0A CP LF +0216 0074 28 FB JR Z,RDCHR ; Ignore LF +0217 0076 FE 1B CP ESC +0218 0078 20 02 JR NZ,RDCHR1 +0219 007A 3E 03 LD A,CTRLC ; Change ESC to CTRL-C +0220 007C C9 RDCHR1 RET +0221 007D +0222 007D FE 0D WRCHR CP CR +0223 007F 28 0A JR Z,WRCRLF ; When CR, write CRLF +0224 0081 FE 0C CP M_CLS +0225 0083 28 04 JR Z,WR ; Allow write of "CLS" +0226 0085 FE 20 CP ' ' ; Don't write out any other control codes +0227 0087 38 01 JR C,NOWR ; ie. < space +0228 0089 CF WR RST 08H +0229 008A C9 NOWR RET +0230 008B +0231 008B 3E 0D WRCRLF LD A,CR +0232 008D CF RST 08H +0233 008E 3E 0A LD A,LF +0234 0090 CF RST 08H +0235 0091 3E 0D LD A,CR +0236 0093 C9 RET +0237 0094 +0238 0094 +0239 0094 ;------------------------------------------------------------------------------ +0240 0094 ; Initialise hardware and start main loop +0241 0094 ;------------------------------------------------------------------------------ +0242 0094 31 28 30 M_INIT LD SP,M_STACK ; Set the Stack Pointer +0243 0097 +0244 0097 3E 95 LD A,RTS_LOW +0245 0099 D3 80 OUT (ACIA0_C),A ; Initialise ACIA0 +0246 009B D3 82 OUT (ACIA1_C),A ; Initialise ACIA1 +0247 009D ; Display the "Press space to start" message on both consoles +0248 009D 3E 00 LD A,$00 +0249 009F 32 00 30 LD (primaryIO),A +0250 00A2 21 65 03 LD HL,INITTXT +0251 00A5 CD 1B 01 CALL M_PRINT +0252 00A8 3E 01 LD A,$01 +0253 00AA 32 00 30 LD (primaryIO),A +0254 00AD 21 65 03 LD HL,INITTXT +0255 00B0 CD 1B 01 CALL M_PRINT +0256 00B3 +0257 00B3 ; Wait until space is in one of the buffers to determine the active console +0258 00B3 +0259 00B3 waitForSpace: +0260 00B3 +0261 00B3 CD 63 00 CALL ckincharA +0262 00B6 28 0F jr Z,notInA +0263 00B8 3E 00 LD A,$00 +0264 00BA 32 00 30 LD (primaryIO),A +0265 00BD CD 1B 00 CALL conin +0266 00C0 FE 20 CP ' ' +0267 00C2 C2 B3 00 JP NZ, waitForSpace +0268 00C5 18 14 JR spacePressed +0269 00C7 +0270 00C7 notInA: +0271 00C7 CD 6A 00 CALL ckincharB +0272 00CA 28 E7 JR Z,waitForSpace +0273 00CC 3E 01 LD A,$01 +0274 00CE 32 00 30 LD (primaryIO),A +0275 00D1 CD 1B 00 CALL conin +0276 00D4 FE 20 CP ' ' +0277 00D6 C2 B3 00 JP NZ, waitForSpace +0278 00D9 18 00 JR spacePressed +0279 00DB +0280 00DB spacePressed: +0281 00DB +0282 00DB ; Clear message on both consoles +0283 00DB 3E 0C LD A,$0C +0284 00DD CD 3C 00 CALL conoutA +0285 00E0 CD 46 00 CALL conoutB +0286 00E3 +0287 00E3 ; primaryIO is now set to the channel where SPACE was pressed +0288 00E3 +0289 00E3 +0290 00E3 CD 22 01 CALL TXCRLF ; TXCRLF +0291 00E6 21 96 02 LD HL,M_SIGNON ; Print SIGNON message +0292 00E9 CD 1B 01 CALL M_PRINT +0293 00EC +0294 00EC ;------------------------------------------------------------------------------ +0295 00EC ; Monitor command loop +0296 00EC ;------------------------------------------------------------------------------ +0297 00EC 21 EC 00 MAIN LD HL,MAIN ; Save entry point for Monitor +0298 00EF E5 PUSH HL ; This is the return address +0299 00F0 CD 22 01 MAIN0 CALL TXCRLF ; Entry point for Monitor, Normal +0300 00F3 3E 3E LD A,'>' ; Get a ">" +0301 00F5 CF RST 08H ; print it +0302 00F6 +0303 00F6 CD 71 00 MAIN1 CALL RDCHR ; Get a character from the input port +0304 00F9 FE 20 CP ' ' ; or less? +0305 00FB 38 F9 JR C,MAIN1 ; Go back +0306 00FD +0307 00FD FE 3A CP ':' ; ":"? +0308 00FF CA 99 01 JP Z,LOAD ; First character of a HEX load +0309 0102 +0310 0102 CD 7D 00 CALL WRCHR ; Print char on console +0311 0105 +0312 0105 E6 5F AND $5F ; Make character uppercase +0313 0107 +0314 0107 FE 42 CP 'B' +0315 0109 CA DA 01 JP Z,BASIC +0316 010C +0317 010C FE 47 CP 'G' +0318 010E CA 93 01 JP Z,M_GOTO +0319 0111 +0320 0111 FE 58 CP 'X' +0321 0113 CA F1 01 JP Z,CPMLOAD +0322 0116 +0323 0116 3E 3F LD A,'?' ; Get a "?" +0324 0118 CF RST 08H ; Print it +0325 0119 18 D5 JR MAIN0 +0326 011B +0327 011B ;------------------------------------------------------------------------------ +0328 011B ; Print string of characters to Serial A until byte=$00, WITH CR, LF +0329 011B ;------------------------------------------------------------------------------ +0330 011B 7E M_PRINT LD A,(HL) ; Get character +0331 011C B7 OR A ; Is it $00 ? +0332 011D C8 RET Z ; Then RETurn on terminator +0333 011E CF RST 08H ; Print it +0334 011F 23 INC HL ; Next Character +0335 0120 18 F9 JR M_PRINT ; Continue until $00 +0336 0122 +0337 0122 +0338 0122 3E 0D TXCRLF LD A,$0D ; +0339 0124 CF RST 08H ; Print character +0340 0125 3E 0A LD A,$0A ; +0341 0127 CF RST 08H ; Print character +0342 0128 C9 RET +0343 0129 +0344 0129 ;------------------------------------------------------------------------------ +0345 0129 ; Get a character from the console, must be $20-$7F to be valid (no control characters) +0346 0129 ; and breaks with the Zero Flag set +0347 0129 ;------------------------------------------------------------------------------ +0348 0129 CD 71 00 M_GETCHR CALL RDCHR ; RX a Character +0349 012C FE 03 CP $03 ; User break? +0350 012E C8 RET Z +0351 012F FE 20 CP $20 ; or better? +0352 0131 38 F6 JR C,M_GETCHR ; Do it again until we get something usable +0353 0133 C9 RET +0354 0134 ;------------------------------------------------------------------------------ +0355 0134 ; Gets two ASCII characters from the console (assuming them to be HEX 0-9 A-F) +0356 0134 ; Moves them into B and C, converts them into a byte value in A and updates a +0357 0134 ; Checksum value in E +0358 0134 ;------------------------------------------------------------------------------ +0359 0134 CD 29 01 GET2 CALL M_GETCHR ; Get us a valid character to work with +0360 0137 47 LD B,A ; Load it in B +0361 0138 CD 29 01 CALL M_GETCHR ; Get us another character +0362 013B 4F LD C,A ; load it in C +0363 013C CD 73 01 CALL BCTOA ; Convert ASCII to byte +0364 013F 4F LD C,A ; Build the checksum +0365 0140 7B LD A,E +0366 0141 91 SUB C ; The checksum should always equal zero when checked +0367 0142 5F LD E,A ; Save the checksum back where it came from +0368 0143 79 LD A,C ; Retrieve the byte and go back +0369 0144 C9 RET +0370 0145 ;------------------------------------------------------------------------------ +0371 0145 ; Gets four Hex characters from the console, converts them to values in HL +0372 0145 ;------------------------------------------------------------------------------ +0373 0145 21 00 00 GETHL LD HL,$0000 ; Gets xxxx but sets Carry Flag on any Terminator +0374 0148 CD 8C 01 CALL ECHO ; RX a Character +0375 014B FE 0D CP $0D ; ? +0376 014D 20 0E JR NZ,GETX2 ; other key +0377 014F 37 SETCY SCF ; Set Carry Flag +0378 0150 C9 RET ; and Return to main program +0379 0151 ;------------------------------------------------------------------------------ +0380 0151 ; This routine converts last four hex characters (0-9 A-F) user types into a value in HL +0381 0151 ; Rotates the old out and replaces with the new until the user hits a terminating character +0382 0151 ;------------------------------------------------------------------------------ +0383 0151 21 00 00 GETX LD HL,$0000 ; CLEAR HL +0384 0154 CD 8C 01 GETX1 CALL ECHO ; RX a character from the console +0385 0157 FE 0D CP $0D ; +0386 0159 C8 RET Z ; quit +0387 015A FE 2C CP $2C ; <,> can be used to safely quit for multiple entries +0388 015C C8 RET Z ; (Like filling both DE and HL from the user) +0389 015D FE 03 GETX2 CP $03 ; Likewise, a will terminate clean, too, but +0390 015F 28 EE JR Z,SETCY ; It also sets the Carry Flag for testing later. +0391 0161 29 ADD HL,HL ; Otherwise, rotate the previous low nibble to high +0392 0162 29 ADD HL,HL ; rather slowly +0393 0163 29 ADD HL,HL ; until we get to the top +0394 0164 29 ADD HL,HL ; and then we can continue on. +0395 0165 D6 30 SUB $30 ; Convert ASCII to byte value +0396 0167 FE 0A CP $0A ; Are we in the 0-9 range? +0397 0169 38 02 JR C,GETX3 ; Then we just need to sub $30, but if it is A-F +0398 016B D6 07 SUB $07 ; We need to take off 7 more to get the value down to +0399 016D E6 0F GETX3 AND $0F ; to the right hex value +0400 016F 85 ADD A,L ; Add the high nibble to the low +0401 0170 6F LD L,A ; Move the byte back to A +0402 0171 18 E1 JR GETX1 ; and go back for next character until he terminates +0403 0173 ;------------------------------------------------------------------------------ +0404 0173 ; Convert ASCII characters in B C registers to a byte value in A +0405 0173 ;------------------------------------------------------------------------------ +0406 0173 78 BCTOA LD A,B ; Move the hi order byte to A +0407 0174 D6 30 SUB $30 ; Take it down from Ascii +0408 0176 FE 0A CP $0A ; Are we in the 0-9 range here? +0409 0178 38 02 JR C,BCTOA1 ; If so, get the next nybble +0410 017A D6 07 SUB $07 ; But if A-F, take it down some more +0411 017C 07 BCTOA1 RLCA ; Rotate the nybble from low to high +0412 017D 07 RLCA ; One bit at a time +0413 017E 07 RLCA ; Until we +0414 017F 07 RLCA ; Get there with it +0415 0180 47 LD B,A ; Save the converted high nybble +0416 0181 79 LD A,C ; Now get the low order byte +0417 0182 D6 30 SUB $30 ; Convert it down from Ascii +0418 0184 FE 0A CP $0A ; 0-9 at this point? +0419 0186 38 02 JR C,BCTOA2 ; Good enough then, but +0420 0188 D6 07 SUB $07 ; Take off 7 more if it's A-F +0421 018A 80 BCTOA2 ADD A,B ; Add in the high order nybble +0422 018B C9 RET +0423 018C +0424 018C ;------------------------------------------------------------------------------ +0425 018C ; Get a character and echo it back to the user +0426 018C ;------------------------------------------------------------------------------ +0427 018C CD 71 00 ECHO CALL RDCHR +0428 018F CD 7D 00 CALL WRCHR +0429 0192 C9 RET +0430 0193 +0431 0193 ;------------------------------------------------------------------------------ +0432 0193 ; GOTO command +0433 0193 ;------------------------------------------------------------------------------ +0434 0193 CD 45 01 M_GOTO CALL GETHL ; ENTRY POINT FOR oto addr. Get XXXX from user. +0435 0196 D8 RET C ; Return if invalid +0436 0197 E5 PUSH HL +0437 0198 C9 RET ; Jump to HL address value +0438 0199 +0439 0199 ;------------------------------------------------------------------------------ +0440 0199 ; LOAD Intel Hex format file from the console. +0441 0199 ; [Intel Hex Format is: +0442 0199 ; 1) Colon (Frame 0) +0443 0199 ; 2) Record Length Field (Frames 1 and 2) +0444 0199 ; 3) Load Address Field (Frames 3,4,5,6) +0445 0199 ; 4) Record Type Field (Frames 7 and 8) +0446 0199 ; 5) Data Field (Frames 9 to 9+2*(Record Length)-1 +0447 0199 ; 6) Checksum Field - Sum of all byte values from Record Length to and +0448 0199 ; including Checksum Field = 0 ] +0449 0199 ;------------------------------------------------------------------------------ +0450 0199 1E 00 LOAD LD E,0 ; First two Characters is the Record Length Field +0451 019B CD 34 01 CALL GET2 ; Get us two characters into BC, convert it to a byte +0452 019E 57 LD D,A ; Load Record Length count into D +0453 019F CD 34 01 CALL GET2 ; Get next two characters, Memory Load Address +0454 01A2 67 LD H,A ; put value in H register. +0455 01A3 CD 34 01 CALL GET2 ; Get next two characters, Memory Load Address +0456 01A6 6F LD L,A ; put value in L register. +0457 01A7 CD 34 01 CALL GET2 ; Get next two characters, Record Field Type +0458 01AA FE 01 CP $01 ; Record Field Type 00 is Data, 01 is End of File +0459 01AC 20 09 JR NZ,LOAD2 ; Must be the end of that file +0460 01AE CD 34 01 CALL GET2 ; Get next two characters, assemble into byte +0461 01B1 7B LD A,E ; Recall the Checksum byte +0462 01B2 A7 AND A ; Is it Zero? +0463 01B3 28 1E JR Z,LOAD00 ; Print footer reached message +0464 01B5 18 15 JR LOADERR ; Checksums don't add up, Error out +0465 01B7 +0466 01B7 7A LOAD2 LD A,D ; Retrieve line character counter +0467 01B8 A7 AND A ; Are we done with this line? +0468 01B9 28 0B JR Z,LOAD3 ; Get two more ascii characters, build a byte and checksum +0469 01BB CD 34 01 CALL GET2 ; Get next two chars, convert to byte in A, checksum it +0470 01BE 77 LD (HL),A ; Move converted byte in A to memory location +0471 01BF 23 INC HL ; Increment pointer to next memory location +0472 01C0 3E 2E LD A,'.' ; Print out a "." for every byte loaded +0473 01C2 CF RST 08H ; +0474 01C3 15 DEC D ; Decrement line character counter +0475 01C4 18 F1 JR LOAD2 ; and keep loading into memory until line is complete +0476 01C6 +0477 01C6 CD 34 01 LOAD3 CALL GET2 ; Get two chars, build byte and checksum +0478 01C9 7B LD A,E ; Check the checksum value +0479 01CA A7 AND A ; Is it zero? +0480 01CB C8 RET Z +0481 01CC +0482 01CC 21 54 03 LOADERR LD HL,CKSUMERR ; Get "Checksum Error" message +0483 01CF CD 1B 01 CALL M_PRINT ; Print Message from (HL) and terminate the load +0484 01D2 C9 RET +0485 01D3 +0486 01D3 21 8A 03 LOAD00 LD HL,LDETXT ; Print load complete message +0487 01D6 CD 1B 01 CALL M_PRINT +0488 01D9 C9 RET +0489 01DA +0490 01DA ;------------------------------------------------------------------------------ +0491 01DA ; Start BASIC command +0492 01DA ;------------------------------------------------------------------------------ +0493 01DA BASIC +0494 01DA 21 42 03 LD HL,M_BASTXT +0495 01DD CD 1B 01 CALL M_PRINT +0496 01E0 CD 29 01 CALL M_GETCHR +0497 01E3 C8 RET Z ; Cancel if CTRL-C +0498 01E4 E6 5F AND $5F ; uppercase +0499 01E6 FE 43 CP 'C' +0500 01E8 CA 95 03 JP Z,COLD +0501 01EB FE 57 CP 'W' +0502 01ED CA 98 03 JP Z,WARM +0503 01F0 C9 RET +0504 01F1 +0505 01F1 ;------------------------------------------------------------------------------ +0506 01F1 ; CP/M load command +0507 01F1 ;------------------------------------------------------------------------------ +0508 01F1 CPMLOAD +0509 01F1 +0510 01F1 21 03 02 LD HL,CPMTXT +0511 01F4 CD 1B 01 CALL M_PRINT +0512 01F7 CD 29 01 CALL M_GETCHR +0513 01FA C8 RET Z ; Cancel if CTRL-C +0514 01FB E6 5F AND $5F ; uppercase +0515 01FD FE 59 CP 'Y' +0516 01FF CA 24 02 JP Z,CPMLOAD2 +0517 0202 C9 RET +0518 0203 CPMTXT +0519 0203 0D 0A .BYTE $0D,$0A +0520 0205 426F6F742043 .TEXT "Boot CP/M?" +0520 020B 502F4D3F +0521 020F 00 .BYTE $00 +0522 0210 +0523 0210 CPMTXT2 +0524 0210 0D 0A .BYTE $0D,$0A +0525 0212 4C6F6164696E .TEXT "Loading CP/M..." +0525 0218 672043502F4D2E2E2E +0526 0221 0D 0A 00 .BYTE $0D,$0A,$00 +0527 0224 +0528 0224 CPMLOAD2 +0529 0224 21 10 02 LD HL,CPMTXT2 +0530 0227 CD 1B 01 CALL M_PRINT +0531 022A +0532 022A 06 18 LD B,numSecs +0533 022C +0534 022C 3E 00 LD A,0 +0535 022E 32 04 30 LD (lba0),A +0536 0231 32 05 30 ld (lba1),A +0537 0234 32 06 30 ld (lba2),A +0538 0237 32 07 30 ld (lba3),A +0539 023A +0540 023A 21 00 D0 LD HL,loadAddr +0541 023D 22 02 30 LD (dmaAddr),HL +0542 0240 processSectors: +0543 0240 +0544 0240 CD 6E 02 call readhst +0545 0243 +0546 0243 11 00 02 LD DE,0200H +0547 0246 2A 02 30 LD HL,(dmaAddr) +0548 0249 19 ADD HL,DE +0549 024A 22 02 30 LD (dmaAddr),HL +0550 024D 3A 04 30 LD A,(lba0) +0551 0250 3C INC A +0552 0251 32 04 30 LD (lba0),A +0553 0254 +0554 0254 10 EA djnz processSectors +0555 0256 +0556 0256 ; Start CP/M using entry at top of BIOS +0557 0256 ; The current active console stream ID is pushed onto the stack +0558 0256 ; to allow the CBIOS to pick it up +0559 0256 ; 0 = ACIA0, 1 = ACIA1 +0560 0256 +0561 0256 3A 00 30 ld A,(primaryIO) +0562 0259 F5 PUSH AF +0563 025A 2A FE FF ld HL,($FFFE) +0564 025D E9 jp (HL) +0565 025E +0566 025E +0567 025E ;------------------------------------------------------------------------------ +0568 025E ; ROUTINES AS USED IN BIOS +0569 025E ;------------------------------------------------------------------------------ +0570 025E +0571 025E ;================================================================================================ +0572 025E ; Convert track/head/sector into LBA for physical access to the disk +0573 025E ;================================================================================================ +0574 025E setLBAaddr: +0575 025E ; Transfer LBA to disk (LBA3 not used on SD card) +0576 025E 3A 06 30 LD A,(lba2) +0577 0261 D3 8C OUT (SD_LBA2),A +0578 0263 3A 05 30 LD A,(lba1) +0579 0266 D3 8B OUT (SD_LBA1),A +0580 0268 3A 04 30 LD A,(lba0) +0581 026B D3 8A OUT (SD_LBA0),A +0582 026D C9 RET +0583 026E +0584 026E ;================================================================================================ +0585 026E ; Read physical sector from host +0586 026E ;================================================================================================ +0587 026E +0588 026E readhst: +0589 026E F5 PUSH AF +0590 026F C5 PUSH BC +0591 0270 E5 PUSH HL +0592 0271 +0593 0271 DB 89 rdWait1: IN A,(SD_STATUS) +0594 0273 FE 80 CP 128 +0595 0275 20 FA JR NZ,rdWait1 +0596 0277 +0597 0277 CD 5E 02 CALL setLBAaddr +0598 027A +0599 027A 3E 00 LD A,$00 ; 00 = Read block +0600 027C D3 89 OUT (SD_CONTROL),A +0601 027E +0602 027E 0E 04 LD c,4 +0603 0280 ; LD HL,hstbuf +0604 0280 rd4secs: +0605 0280 06 80 LD b,128 +0606 0282 rdByte: +0607 0282 +0608 0282 DB 89 rdWait2: IN A,(SD_STATUS) +0609 0284 FE E0 CP 224 ; Read byte waiting +0610 0286 20 FA JR NZ,rdWait2 +0611 0288 +0612 0288 DB 88 IN A,(SD_DATA) +0613 028A +0614 028A 77 LD (HL),A +0615 028B 23 INC HL +0616 028C 05 dec b +0617 028D 20 F3 JR NZ, rdByte +0618 028F 0D dec c +0619 0290 20 EE JR NZ,rd4secs +0620 0292 +0621 0292 E1 POP HL +0622 0293 C1 POP BC +0623 0294 F1 POP AF +0624 0295 +0625 0295 ; XOR a +0626 0295 ; ld (erflag),a +0627 0295 C9 RET +0628 0296 +0629 0296 ;------------------------------------------------------------------------------ +0630 0296 ; END OF ROUTINES AS USED IN BIOS +0631 0296 ;------------------------------------------------------------------------------ +0632 0296 +0633 0296 +0634 0296 43502F4D2042M_SIGNON .BYTE "CP/M Boot ROM 2.0" +0634 029C 6F6F7420524F4D20322E30 +0635 02A7 20627920472E .BYTE " by G. Searle" +0635 02AD 20536561726C65 +0636 02B4 0D 0A .BYTE $0D,$0A +0637 02B6 0D 0A .BYTE $0D,$0A +0638 02B8 4243206F7220 .TEXT "BC or BW - ROM BASIC Cold/Warm" +0638 02BE 4257202D20524F4D20424153494320436F6C642F5761726D +0639 02D6 0D 0A .BYTE $0D,$0A +0640 02D8 582020202020 .TEXT "X - Boot CP/M (load $D000-$FFFF)" +0640 02DE 2020202D20426F6F742043502F4D20286C6F61642024443030302D244646464629 +0641 02FF 0D 0A .BYTE $0D,$0A +0642 0301 3A6E6E6E6E2E .TEXT ":nnnn... - Load Intel-Hex file record" +0642 0307 2E2E202D204C6F616420496E74656C2D4865782066696C65207265636F7264 +0643 0326 0D 0A .BYTE $0D,$0A +0644 0328 476E6E6E6E20 .TEXT "Gnnnn - Run loc nnnn" +0644 032E 2020202D2052756E206C6F63206E6E6E6E +0645 033F 0D 0A .BYTE $0D,$0A +0646 0341 00 .BYTE $00 +0647 0342 +0648 0342 M_BASTXT +0649 0342 0D 0A .BYTE $0D,$0A +0650 0344 436F6C64206F .TEXT "Cold or warm?" +0650 034A 72207761726D3F +0651 0351 0D 0A 00 .BYTE $0D,$0A,$00 +0652 0354 +0653 0354 436865636B73CKSUMERR .BYTE "Checksum error" +0653 035A 756D206572726F72 +0654 0362 0D 0A 00 .BYTE $0D,$0A,$00 +0655 0365 +0656 0365 INITTXT +0657 0365 0C .BYTE $0C +0658 0366 507265737320 .TEXT "Press [SPACE] to activate console" +0658 036C 5B53504143455D20746F20616374697661746520636F6E736F6C65 +0659 0387 0D 0A 00 .BYTE $0D,$0A, $00 +0660 038A +0661 038A LDETXT +0662 038A 436F6D706C65 .TEXT "Complete" +0662 0390 7465 +0663 0392 0D 0A 00 .BYTE $0D,$0A, $00 +0664 0395 +0665 0395 ;=========================================================================================================================== +0666 0395 +0667 0395 ; NASCOM ROM BASIC Ver 4.7, (C) 1978 Microsoft +0668 0395 ; Scanned from source published in 80-BUS NEWS from Vol 2, Issue 3 +0669 0395 ; (May-June 1983) to Vol 3, Issue 3 (May-June 1984) +0670 0395 ; Adapted for the freeware Zilog Macro Assembler 2.10 to produce +0671 0395 ; the original ROM code (checksum A934H). PA +0672 0395 +0673 0395 ; GENERAL EQUATES +0674 0395 +0675 0395 CTRLC .EQU 03H ; Control "C" +0676 0395 CTRLG .EQU 07H ; Control "G" +0677 0395 BKSP .EQU 08H ; Back space +0678 0395 LF .EQU 0AH ; Line feed +0679 0395 CS .EQU 0CH ; Clear screen +0680 0395 CR .EQU 0DH ; Carriage return +0681 0395 CTRLO .EQU 0FH ; Control "O" +0682 0395 CTRLQ .EQU 11H ; Control "Q" +0683 0395 CTRLR .EQU 12H ; Control "R" +0684 0395 CTRLS .EQU 13H ; Control "S" +0685 0395 CTRLU .EQU 15H ; Control "U" +0686 0395 ESC .EQU 1BH ; Escape +0687 0395 DEL .EQU 7FH ; Delete +0688 0395 +0689 0395 ; BASIC WORK SPACE LOCATIONS +0690 0395 +0691 0395 WRKSPC .EQU 30B0H ; BASIC Work space +0692 0395 USR .EQU WRKSPC+3H ; "USR (x)" jump +0693 0395 OUTSUB .EQU WRKSPC+6H ; "OUT p,n" +0694 0395 OTPORT .EQU WRKSPC+7H ; Port (p) +0695 0395 DIVSUP .EQU WRKSPC+9H ; Division support routine +0696 0395 DIV1 .EQU WRKSPC+0AH ; <- Values +0697 0395 DIV2 .EQU WRKSPC+0EH ; <- to +0698 0395 DIV3 .EQU WRKSPC+12H ; <- be +0699 0395 DIV4 .EQU WRKSPC+15H ; <-inserted +0700 0395 SEED .EQU WRKSPC+17H ; Random number seed +0701 0395 LSTRND .EQU WRKSPC+3AH ; Last random number +0702 0395 INPSUB .EQU WRKSPC+3EH ; #INP (x)" Routine +0703 0395 INPORT .EQU WRKSPC+3FH ; PORT (x) +0704 0395 NULLS .EQU WRKSPC+41H ; Number of nulls +0705 0395 LWIDTH .EQU WRKSPC+42H ; Terminal width +0706 0395 COMMAN .EQU WRKSPC+43H ; Width for commas +0707 0395 NULFLG .EQU WRKSPC+44H ; Null after input byte flag +0708 0395 CTLOFG .EQU WRKSPC+45H ; Control "O" flag +0709 0395 LINESC .EQU WRKSPC+46H ; Lines counter +0710 0395 LINESN .EQU WRKSPC+48H ; Lines number +0711 0395 CHKSUM .EQU WRKSPC+4AH ; Array load/save check sum +0712 0395 NMIFLG .EQU WRKSPC+4CH ; Flag for NMI break routine +0713 0395 BRKFLG .EQU WRKSPC+4DH ; Break flag +0714 0395 RINPUT .EQU WRKSPC+4EH ; Input reflection +0715 0395 POINT .EQU WRKSPC+51H ; "POINT" reflection (unused) +0716 0395 PSET .EQU WRKSPC+54H ; "SET" reflection +0717 0395 RESET .EQU WRKSPC+57H ; "RESET" reflection +0718 0395 STRSPC .EQU WRKSPC+5AH ; Bottom of string space +0719 0395 LINEAT .EQU WRKSPC+5CH ; Current line number +0720 0395 BASTXT .EQU WRKSPC+5EH ; Pointer to start of program +0721 0395 BUFFER .EQU WRKSPC+61H ; Input buffer +0722 0395 STACK .EQU WRKSPC+66H ; Initial stack +0723 0395 CURPOS .EQU WRKSPC+0ABH ; Character position on line +0724 0395 LCRFLG .EQU WRKSPC+0ACH ; Locate/Create flag +0725 0395 TYPE .EQU WRKSPC+0ADH ; Data type flag +0726 0395 DATFLG .EQU WRKSPC+0AEH ; Literal statement flag +0727 0395 LSTRAM .EQU WRKSPC+0AFH ; Last available RAM +0728 0395 TMSTPT .EQU WRKSPC+0B1H ; Temporary string pointer +0729 0395 TMSTPL .EQU WRKSPC+0B3H ; Temporary string pool +0730 0395 TMPSTR .EQU WRKSPC+0BFH ; Temporary string +0731 0395 STRBOT .EQU WRKSPC+0C3H ; Bottom of string space +0732 0395 CUROPR .EQU WRKSPC+0C5H ; Current operator in EVAL +0733 0395 LOOPST .EQU WRKSPC+0C7H ; First statement of loop +0734 0395 DATLIN .EQU WRKSPC+0C9H ; Line of current DATA item +0735 0395 FORFLG .EQU WRKSPC+0CBH ; "FOR" loop flag +0736 0395 LSTBIN .EQU WRKSPC+0CCH ; Last byte entered +0737 0395 READFG .EQU WRKSPC+0CDH ; Read/Input flag +0738 0395 BRKLIN .EQU WRKSPC+0CEH ; Line of break +0739 0395 NXTOPR .EQU WRKSPC+0D0H ; Next operator in EVAL +0740 0395 ERRLIN .EQU WRKSPC+0D2H ; Line of error +0741 0395 CONTAD .EQU WRKSPC+0D4H ; Where to CONTinue +0742 0395 PROGND .EQU WRKSPC+0D6H ; End of program +0743 0395 VAREND .EQU WRKSPC+0D8H ; End of variables +0744 0395 ARREND .EQU WRKSPC+0DAH ; End of arrays +0745 0395 NXTDAT .EQU WRKSPC+0DCH ; Next data item +0746 0395 FNRGNM .EQU WRKSPC+0DEH ; Name of FN argument +0747 0395 FNARG .EQU WRKSPC+0E0H ; FN argument value +0748 0395 FPREG .EQU WRKSPC+0E4H ; Floating point register +0749 0395 FPEXP .EQU FPREG+3 ; Floating point exponent +0750 0395 SGNRES .EQU WRKSPC+0E8H ; Sign of result +0751 0395 PBUFF .EQU WRKSPC+0E9H ; Number print buffer +0752 0395 MULVAL .EQU WRKSPC+0F6H ; Multiplier +0753 0395 PROGST .EQU WRKSPC+0F9H ; Start of program text area +0754 0395 STLOOK .EQU WRKSPC+15DH ; Start of memory test +0755 0395 +0756 0395 ; BASIC ERROR CODE VALUES +0757 0395 +0758 0395 NF .EQU 00H ; NEXT without FOR +0759 0395 SN .EQU 02H ; Syntax error +0760 0395 RG .EQU 04H ; RETURN without GOSUB +0761 0395 OD .EQU 06H ; Out of DATA +0762 0395 FC .EQU 08H ; Function call error +0763 0395 OV .EQU 0AH ; Overflow +0764 0395 OM .EQU 0CH ; Out of memory +0765 0395 UL .EQU 0EH ; Undefined line number +0766 0395 BS .EQU 10H ; Bad subscript +0767 0395 DD .EQU 12H ; Re-DIMensioned array +0768 0395 DZ .EQU 14H ; Division by zero (/0) +0769 0395 ID .EQU 16H ; Illegal direct +0770 0395 TM .EQU 18H ; Type miss-match +0771 0395 OS .EQU 1AH ; Out of string space +0772 0395 LS .EQU 1CH ; String too long +0773 0395 ST .EQU 1EH ; String formula too complex +0774 0395 CN .EQU 20H ; Can't CONTinue +0775 0395 UF .EQU 22H ; UnDEFined FN function +0776 0395 MO .EQU 24H ; Missing operand +0777 0395 HX .EQU 26H ; HEX error +0778 0395 BN .EQU 28H ; BIN error +0779 0395 +0780 0395 ; .ORG 00396H +0781 0395 +0782 0395 C3 9B 03 COLD: JP STARTB ; Jump for cold start +0783 0398 C3 39 04 WARM: JP WARMST ; Jump for warm start +0784 039B STARTB: +0785 039B DD 21 00 00 LD IX,0 ; Flag cold start +0786 039F C3 A6 03 JP CSTART ; Jump to initialise +0787 03A2 +0788 03A2 4C 0C .WORD DEINT ; Get integer -32768 to 32767 +0789 03A4 C2 13 .WORD ABPASS ; Return integer in AB +0790 03A6 +0791 03A6 +0792 03A6 21 B0 30 CSTART: LD HL,WRKSPC ; Start of workspace RAM +0793 03A9 F9 LD SP,HL ; Set up a temporary stack +0794 03AA C3 E1 1F JP INITST ; Go to initialise +0795 03AD +0796 03AD 11 73 06 INIT: LD DE,INITAB ; Initialise workspace +0797 03B0 06 63 LD B,INITBE-INITAB+3; Bytes to copy +0798 03B2 21 B0 30 LD HL,WRKSPC ; Into workspace RAM +0799 03B5 1A COPY: LD A,(DE) ; Get source +0800 03B6 77 LD (HL),A ; To destination +0801 03B7 23 INC HL ; Next destination +0802 03B8 13 INC DE ; Next source +0803 03B9 05 DEC B ; Count bytes +0804 03BA C2 B5 03 JP NZ,COPY ; More to move +0805 03BD F9 LD SP,HL ; Temporary stack +0806 03BE CD 74 08 CALL CLREG ; Clear registers and stack +0807 03C1 CD 42 0E CALL PRNTCRLF ; Output CRLF +0808 03C4 32 5A 31 LD (BUFFER+72+1),A ; Mark end of buffer +0809 03C7 32 A9 31 LD (PROGST),A ; Initialise program area +0810 03CA 21 88 04 MSIZE: LD HL,MEMMSG ; Point to message +0811 03CD CD E0 14 CALL PRS ; Output "Memory size" +0812 03D0 CD 91 08 CALL PROMPT ; Get input with '?' +0813 03D3 CD 9A 0B CALL GETCHR ; Get next character +0814 03D6 B7 OR A ; Set flags +0815 03D7 C2 EF 03 JP NZ,TSTMEM ; If number - Test if RAM there +0816 03DA 21 0D 32 LD HL,STLOOK ; Point to start of RAM +0817 03DD 23 MLOOP: INC HL ; Next byte +0818 03DE 7C LD A,H ; Above address FFFF ? +0819 03DF B5 OR L +0820 03E0 CA 01 04 JP Z,SETTOP ; Yes - 64K RAM +0821 03E3 7E LD A,(HL) ; Get contents +0822 03E4 47 LD B,A ; Save it +0823 03E5 2F CPL ; Flip all bits +0824 03E6 77 LD (HL),A ; Put it back +0825 03E7 BE CP (HL) ; RAM there if same +0826 03E8 70 LD (HL),B ; Restore old contents +0827 03E9 CA DD 03 JP Z,MLOOP ; If RAM - test next byte +0828 03EC C3 01 04 JP SETTOP ; Top of RAM found +0829 03EF +0830 03EF CD 66 0C TSTMEM: CALL ATOH ; Get high memory into DE +0831 03F2 B7 OR A ; Set flags on last byte +0832 03F3 C2 42 07 JP NZ,SNERR ; ?SN Error if bad character +0833 03F6 EB EX DE,HL ; Address into HL +0834 03F7 2B DEC HL ; Back one byte +0835 03F8 3E D9 LD A,11011001B ; Test byte +0836 03FA 46 LD B,(HL) ; Get old contents +0837 03FB 77 LD (HL),A ; Load test byte +0838 03FC BE CP (HL) ; RAM there if same +0839 03FD 70 LD (HL),B ; Restore old contents +0840 03FE C2 CA 03 JP NZ,MSIZE ; Ask again if no RAM +0841 0401 +0842 0401 2B SETTOP: DEC HL ; Back one byte +0843 0402 11 0C 32 LD DE,STLOOK-1 ; See if enough RAM +0844 0405 CD 0A 0A CALL CPDEHL ; Compare DE with HL +0845 0408 DA CA 03 JP C,MSIZE ; Ask again if not enough RAM +0846 040B 11 CE FF LD DE,0-50 ; 50 Bytes string space +0847 040E 22 5F 31 LD (LSTRAM),HL ; Save last available RAM +0848 0411 19 ADD HL,DE ; Allocate string space +0849 0412 22 0A 31 LD (STRSPC),HL ; Save string space +0850 0415 CD 4F 08 CALL CLRPTR ; Clear program area +0851 0418 2A 0A 31 LD HL,(STRSPC) ; Get end of memory +0852 041B 11 EF FF LD DE,0-17 ; Offset for free bytes +0853 041E 19 ADD HL,DE ; Adjust HL +0854 041F 11 A9 31 LD DE,PROGST ; Start of program text +0855 0422 7D LD A,L ; Get LSB +0856 0423 93 SUB E ; Adjust it +0857 0424 6F LD L,A ; Re-save +0858 0425 7C LD A,H ; Get MSB +0859 0426 9A SBC A,D ; Adjust it +0860 0427 67 LD H,A ; Re-save +0861 0428 E5 PUSH HL ; Save bytes free +0862 0429 21 51 04 LD HL,SIGNON ; Sign-on message +0863 042C CD E0 14 CALL PRS ; Output string +0864 042F E1 POP HL ; Get bytes free back +0865 0430 CD 83 1B CALL PRNTHL ; Output amount of free memory +0866 0433 21 42 04 LD HL,BFREE ; " Bytes free" message +0867 0436 CD E0 14 CALL PRS ; Output string +0868 0439 +0869 0439 31 16 31 WARMST: LD SP,STACK ; Temporary stack +0870 043C CD 74 08 BRKRET: CALL CLREG ; Clear registers and stack +0871 043F C3 8D 07 JP PRNTOK ; Go to get command line +0872 0442 +0873 0442 204279746573BFREE: .BYTE " Bytes free",CR,LF,0,0 +0873 0448 20667265650D0A0000 +0874 0451 +0875 0451 5A3830204241SIGNON: .BYTE "Z80 BASIC Ver 4.7b",CR,LF +0875 0457 5349432056657220342E37620D0A +0876 0465 436F70797269 .BYTE "Copyright ",40,"C",41 +0876 046B 67687420284329 +0877 0472 203139373820 .BYTE " 1978 by Microsoft",CR,LF,0,0 +0877 0478 6279204D6963726F736F66740D0A0000 +0878 0488 +0879 0488 4D656D6F7279MEMMSG: .BYTE "Memory top",0 +0879 048E 20746F7000 +0880 0493 +0881 0493 ; FUNCTION ADDRESS TABLE +0882 0493 +0883 0493 F8 19 FNCTAB: .WORD SGN +0884 0495 BC 1A .WORD INT +0885 0497 0E 1A .WORD ABS +0886 0499 B3 30 .WORD USR +0887 049B A0 13 .WORD FRE +0888 049D 25 17 .WORD INP +0889 049F CE 13 .WORD POS +0890 04A1 82 1C .WORD SQR +0891 04A3 61 1D .WORD RND +0892 04A5 9D 18 .WORD LOG +0893 04A7 D0 1C .WORD EXP +0894 04A9 D6 1D .WORD COS +0895 04AB DC 1D .WORD SIN +0896 04AD 3D 1E .WORD TAN +0897 04AF 52 1E .WORD ATN +0898 04B1 79 17 .WORD PEEK +0899 04B3 BD 1E .WORD DEEK +0900 04B5 01 31 .WORD POINT +0901 04B7 52 16 .WORD LEN +0902 04B9 6A 14 .WORD STR +0903 04BB EC 16 .WORD VAL +0904 04BD 61 16 .WORD ASC +0905 04BF 72 16 .WORD CHR +0906 04C1 DF 1E .WORD HEX +0907 04C3 72 1F .WORD BIN +0908 04C5 82 16 .WORD LEFT +0909 04C7 B2 16 .WORD RIGHT +0910 04C9 BC 16 .WORD MID +0911 04CB +0912 04CB ; RESERVED WORD LIST +0913 04CB +0914 04CB C5 4E 44 WORDS: .BYTE 'E'+80H,"ND" +0915 04CE C6 4F 52 .BYTE 'F'+80H,"OR" +0916 04D1 CE 45 58 54 .BYTE 'N'+80H,"EXT" +0917 04D5 C4 41 54 41 .BYTE 'D'+80H,"ATA" +0918 04D9 C94E505554 .BYTE 'I'+80H,"NPUT" +0919 04DE C4 49 4D .BYTE 'D'+80H,"IM" +0920 04E1 D2 45 41 44 .BYTE 'R'+80H,"EAD" +0921 04E5 CC 45 54 .BYTE 'L'+80H,"ET" +0922 04E8 C7 4F 54 4F .BYTE 'G'+80H,"OTO" +0923 04EC D2 55 4E .BYTE 'R'+80H,"UN" +0924 04EF C9 46 .BYTE 'I'+80H,"F" +0925 04F1 D24553544F52 .BYTE 'R'+80H,"ESTORE" +0925 04F7 45 +0926 04F8 C74F535542 .BYTE 'G'+80H,"OSUB" +0927 04FD D2455455524E .BYTE 'R'+80H,"ETURN" +0928 0503 D2 45 4D .BYTE 'R'+80H,"EM" +0929 0506 D3 54 4F 50 .BYTE 'S'+80H,"TOP" +0930 050A CF 55 54 .BYTE 'O'+80H,"UT" +0931 050D CF 4E .BYTE 'O'+80H,"N" +0932 050F CE 55 4C 4C .BYTE 'N'+80H,"ULL" +0933 0513 D7 41 49 54 .BYTE 'W'+80H,"AIT" +0934 0517 C4 45 46 .BYTE 'D'+80H,"EF" +0935 051A D0 4F 4B 45 .BYTE 'P'+80H,"OKE" +0936 051E C4 4F 4B 45 .BYTE 'D'+80H,"OKE" +0937 0522 D3435245454E .BYTE 'S'+80H,"CREEN" +0938 0528 CC494E4553 .BYTE 'L'+80H,"INES" +0939 052D C3 4C 53 .BYTE 'C'+80H,"LS" +0940 0530 D749445448 .BYTE 'W'+80H,"IDTH" +0941 0535 CD4F4E49544F .BYTE 'M'+80H,"ONITOR" +0941 053B 52 +0942 053C D3 45 54 .BYTE 'S'+80H,"ET" +0943 053F D245534554 .BYTE 'R'+80H,"ESET" +0944 0544 D052494E54 .BYTE 'P'+80H,"RINT" +0945 0549 C3 4F 4E 54 .BYTE 'C'+80H,"ONT" +0946 054D CC 49 53 54 .BYTE 'L'+80H,"IST" +0947 0551 C34C454152 .BYTE 'C'+80H,"LEAR" +0948 0556 C34C4F4144 .BYTE 'C'+80H,"LOAD" +0949 055B C353415645 .BYTE 'C'+80H,"SAVE" +0950 0560 CE 45 57 .BYTE 'N'+80H,"EW" +0951 0563 +0952 0563 D4 41 42 28 .BYTE 'T'+80H,"AB(" +0953 0567 D4 4F .BYTE 'T'+80H,"O" +0954 0569 C6 4E .BYTE 'F'+80H,"N" +0955 056B D3 50 43 28 .BYTE 'S'+80H,"PC(" +0956 056F D4 48 45 4E .BYTE 'T'+80H,"HEN" +0957 0573 CE 4F 54 .BYTE 'N'+80H,"OT" +0958 0576 D3 54 45 50 .BYTE 'S'+80H,"TEP" +0959 057A +0960 057A AB .BYTE '+'+80H +0961 057B AD .BYTE '-'+80H +0962 057C AA .BYTE '*'+80H +0963 057D AF .BYTE '/'+80H +0964 057E DE .BYTE '^'+80H +0965 057F C1 4E 44 .BYTE 'A'+80H,"ND" +0966 0582 CF 52 .BYTE 'O'+80H,"R" +0967 0584 BE .BYTE '>'+80H +0968 0585 BD .BYTE '='+80H +0969 0586 BC .BYTE '<'+80H +0970 0587 +0971 0587 D3 47 4E .BYTE 'S'+80H,"GN" +0972 058A C9 4E 54 .BYTE 'I'+80H,"NT" +0973 058D C1 42 53 .BYTE 'A'+80H,"BS" +0974 0590 D5 53 52 .BYTE 'U'+80H,"SR" +0975 0593 C6 52 45 .BYTE 'F'+80H,"RE" +0976 0596 C9 4E 50 .BYTE 'I'+80H,"NP" +0977 0599 D0 4F 53 .BYTE 'P'+80H,"OS" +0978 059C D3 51 52 .BYTE 'S'+80H,"QR" +0979 059F D2 4E 44 .BYTE 'R'+80H,"ND" +0980 05A2 CC 4F 47 .BYTE 'L'+80H,"OG" +0981 05A5 C5 58 50 .BYTE 'E'+80H,"XP" +0982 05A8 C3 4F 53 .BYTE 'C'+80H,"OS" +0983 05AB D3 49 4E .BYTE 'S'+80H,"IN" +0984 05AE D4 41 4E .BYTE 'T'+80H,"AN" +0985 05B1 C1 54 4E .BYTE 'A'+80H,"TN" +0986 05B4 D0 45 45 4B .BYTE 'P'+80H,"EEK" +0987 05B8 C4 45 45 4B .BYTE 'D'+80H,"EEK" +0988 05BC D04F494E54 .BYTE 'P'+80H,"OINT" +0989 05C1 CC 45 4E .BYTE 'L'+80H,"EN" +0990 05C4 D3 54 52 24 .BYTE 'S'+80H,"TR$" +0991 05C8 D6 41 4C .BYTE 'V'+80H,"AL" +0992 05CB C1 53 43 .BYTE 'A'+80H,"SC" +0993 05CE C3 48 52 24 .BYTE 'C'+80H,"HR$" +0994 05D2 C8 45 58 24 .BYTE 'H'+80H,"EX$" +0995 05D6 C2 49 4E 24 .BYTE 'B'+80H,"IN$" +0996 05DA CC45465424 .BYTE 'L'+80H,"EFT$" +0997 05DF D24947485424 .BYTE 'R'+80H,"IGHT$" +0998 05E5 CD 49 44 24 .BYTE 'M'+80H,"ID$" +0999 05E9 80 .BYTE 80H ; End of list marker +1000 05EA +1001 05EA ; KEYWORD ADDRESS TABLE +1002 05EA +1003 05EA E4 0B WORDTB: .WORD PEND +1004 05EC E1 0A .WORD FOR +1005 05EE BC 0F .WORD NEXT +1006 05F0 31 0D .WORD DATA +1007 05F2 C3 0E .WORD INPUT +1008 05F4 F8 11 .WORD DIM +1009 05F6 F2 0E .WORD READ +1010 05F8 48 0D .WORD LET +1011 05FA EE 0C .WORD GOTO +1012 05FC D1 0C .WORD RUN +1013 05FE C0 0D .WORD IF +1014 0600 AA 0B .WORD RESTOR +1015 0602 DD 0C .WORD GOSUB +1016 0604 0C 0D .WORD RETURN +1017 0606 33 0D .WORD REM +1018 0608 E2 0B .WORD STOP +1019 060A 31 17 .WORD POUT +1020 060C A2 0D .WORD ON +1021 060E 23 0C .WORD NULL +1022 0610 37 17 .WORD WAIT +1023 0612 D6 13 .WORD DEF +1024 0614 80 17 .WORD POKE +1025 0616 C8 1E .WORD DOKE +1026 0618 33 0D .WORD REM +1027 061A AE 1E .WORD LINES +1028 061C A1 1E .WORD CLS +1029 061E A6 1E .WORD WIDTH +1030 0620 DE 1F .WORD MONITR +1031 0622 04 31 .WORD PSET +1032 0624 07 31 .WORD RESET +1033 0626 E4 0D .WORD PRINT +1034 0628 10 0C .WORD CONT +1035 062A 56 0A .WORD LIST +1036 062C 8B 0C .WORD CLEAR +1037 062E 33 0D .WORD REM +1038 0630 33 0D .WORD REM +1039 0632 4E 08 .WORD NEW +1040 0634 +1041 0634 ; RESERVED WORD TOKEN VALUES +1042 0634 +1043 0634 ZEND .EQU 080H ; END +1044 0634 ZFOR .EQU 081H ; FOR +1045 0634 ZDATA .EQU 083H ; DATA +1046 0634 ZGOTO .EQU 088H ; GOTO +1047 0634 ZGOSUB .EQU 08CH ; GOSUB +1048 0634 ZREM .EQU 08EH ; REM +1049 0634 ZPRINT .EQU 09EH ; PRINT +1050 0634 ZNEW .EQU 0A4H ; NEW +1051 0634 +1052 0634 ZTAB .EQU 0A5H ; TAB +1053 0634 ZTO .EQU 0A6H ; TO +1054 0634 ZFN .EQU 0A7H ; FN +1055 0634 ZSPC .EQU 0A8H ; SPC +1056 0634 ZTHEN .EQU 0A9H ; THEN +1057 0634 ZNOT .EQU 0AAH ; NOT +1058 0634 ZSTEP .EQU 0ABH ; STEP +1059 0634 +1060 0634 ZPLUS .EQU 0ACH ; + +1061 0634 ZMINUS .EQU 0ADH ; - +1062 0634 ZTIMES .EQU 0AEH ; * +1063 0634 ZDIV .EQU 0AFH ; / +1064 0634 ZOR .EQU 0B2H ; OR +1065 0634 ZGTR .EQU 0B3H ; > +1066 0634 ZEQUAL .EQU 0B4H ; M +1067 0634 ZLTH .EQU 0B5H ; < +1068 0634 ZSGN .EQU 0B6H ; SGN +1069 0634 ZPOINT .EQU 0C7H ; POINT +1070 0634 ZLEFT .EQU 0CDH +2 ; LEFT$ +1071 0634 +1072 0634 ; ARITHMETIC PRECEDENCE TABLE +1073 0634 +1074 0634 79 PRITAB: .BYTE 79H ; Precedence value +1075 0635 6A 1B .WORD PADD ; FPREG = + FPREG +1076 0637 +1077 0637 79 .BYTE 79H ; Precedence value +1078 0638 9E 17 .WORD PSUB ; FPREG = - FPREG +1079 063A +1080 063A 7C .BYTE 7CH ; Precedence value +1081 063B DC 18 .WORD MULT ; PPREG = * FPREG +1082 063D +1083 063D 7C .BYTE 7CH ; Precedence value +1084 063E 3D 19 .WORD DIV ; FPREG = / FPREG +1085 0640 +1086 0640 7F .BYTE 7FH ; Precedence value +1087 0641 8B 1C .WORD POWER ; FPREG = ^ FPREG +1088 0643 +1089 0643 50 .BYTE 50H ; Precedence value +1090 0644 51 11 .WORD PAND ; FPREG = AND FPREG +1091 0646 +1092 0646 46 .BYTE 46H ; Precedence value +1093 0647 50 11 .WORD POR ; FPREG = OR FPREG +1094 0649 +1095 0649 ; BASIC ERROR CODE LIST +1096 0649 +1097 0649 4E 46 ERRORS: .BYTE "NF" ; NEXT without FOR +1098 064B 53 4E .BYTE "SN" ; Syntax error +1099 064D 52 47 .BYTE "RG" ; RETURN without GOSUB +1100 064F 4F 44 .BYTE "OD" ; Out of DATA +1101 0651 46 43 .BYTE "FC" ; Illegal function call +1102 0653 4F 56 .BYTE "OV" ; Overflow error +1103 0655 4F 4D .BYTE "OM" ; Out of memory +1104 0657 55 4C .BYTE "UL" ; Undefined line +1105 0659 42 53 .BYTE "BS" ; Bad subscript +1106 065B 44 44 .BYTE "DD" ; Re-DIMensioned array +1107 065D 2F 30 .BYTE "/0" ; Division by zero +1108 065F 49 44 .BYTE "ID" ; Illegal direct +1109 0661 54 4D .BYTE "TM" ; Type mis-match +1110 0663 4F 53 .BYTE "OS" ; Out of string space +1111 0665 4C 53 .BYTE "LS" ; String too long +1112 0667 53 54 .BYTE "ST" ; String formula too complex +1113 0669 43 4E .BYTE "CN" ; Can't CONTinue +1114 066B 55 46 .BYTE "UF" ; Undefined FN function +1115 066D 4D 4F .BYTE "MO" ; Missing operand +1116 066F 48 58 .BYTE "HX" ; HEX error +1117 0671 42 4E .BYTE "BN" ; BIN error +1118 0673 +1119 0673 ; INITIALISATION TABLE ------------------------------------------------------- +1120 0673 +1121 0673 C3 39 04 INITAB: JP WARMST ; Warm start jump +1122 0676 C3 61 0C JP FCERR ; "USR (X)" jump (Set to Error) +1123 0679 D3 00 OUT (0),A ; "OUT p,n" skeleton +1124 067B C9 RET +1125 067C D6 00 SUB 0 ; Division support routine +1126 067E 6F LD L,A +1127 067F 7C LD A,H +1128 0680 DE 00 SBC A,0 +1129 0682 67 LD H,A +1130 0683 78 LD A,B +1131 0684 DE 00 SBC A,0 +1132 0686 47 LD B,A +1133 0687 3E 00 LD A,0 +1134 0689 C9 RET +1135 068A 00 00 00 .BYTE 0,0,0 ; Random number seed table used by RND +1136 068D 35 4A CA 99 .BYTE 035H,04AH,0CAH,099H ;-2.65145E+07 +1137 0691 39 1C 76 98 .BYTE 039H,01CH,076H,098H ; 1.61291E+07 +1138 0695 22 95 B3 98 .BYTE 022H,095H,0B3H,098H ;-1.17691E+07 +1139 0699 0A DD 47 98 .BYTE 00AH,0DDH,047H,098H ; 1.30983E+07 +1140 069D 53 D1 99 99 .BYTE 053H,0D1H,099H,099H ;-2-01612E+07 +1141 06A1 0A 1A 9F 98 .BYTE 00AH,01AH,09FH,098H ;-1.04269E+07 +1142 06A5 65 BC CD 98 .BYTE 065H,0BCH,0CDH,098H ;-1.34831E+07 +1143 06A9 D6 77 3E 98 .BYTE 0D6H,077H,03EH,098H ; 1.24825E+07 +1144 06AD 52 C7 4F 80 .BYTE 052H,0C7H,04FH,080H ; Last random number +1145 06B1 DB 00 IN A,(0) ; INP (x) skeleton +1146 06B3 C9 RET +1147 06B4 01 .BYTE 1 ; POS (x) number (1) +1148 06B5 FF .BYTE 255 ; Terminal width (255 = no auto CRLF) +1149 06B6 1C .BYTE 28 ; Width for commas (3 columns) +1150 06B7 00 .BYTE 0 ; No nulls after input bytes +1151 06B8 00 .BYTE 0 ; Output enabled (^O off) +1152 06B9 14 00 .WORD 20 ; Initial lines counter +1153 06BB 14 00 .WORD 20 ; Initial lines number +1154 06BD 00 00 .WORD 0 ; Array load/save check sum +1155 06BF 00 .BYTE 0 ; Break not by NMI +1156 06C0 00 .BYTE 0 ; Break flag +1157 06C1 C3 87 09 JP TTYLIN ; Input reflection (set to TTY) +1158 06C4 C3 00 00 JP $0000 ; POINT reflection unused +1159 06C7 C3 00 00 JP $0000 ; SET reflection +1160 06CA C3 00 00 JP $0000 ; RESET reflection +1161 06CD 0D 32 .WORD STLOOK ; Temp string space +1162 06CF FE FF .WORD -2 ; Current line number (cold) +1163 06D1 AA 31 .WORD PROGST+1 ; Start of program text +1164 06D3 INITBE: +1165 06D3 +1166 06D3 ; END OF INITIALISATION TABLE --------------------------------------------------- +1167 06D3 +1168 06D3 204572726F72ERRMSG: .BYTE " Error",0 +1168 06D9 00 +1169 06DA 20696E2000 INMSG: .BYTE " in ",0 +1170 06DF ZERBYT .EQU $-1 ; A zero byte +1171 06DF 4F6B0D0A0000OKMSG: .BYTE "Ok",CR,LF,0,0 +1172 06E5 427265616B00BRKMSG: .BYTE "Break",0 +1173 06EB +1174 06EB 21 04 00 BAKSTK: LD HL,4 ; Look for "FOR" block with +1175 06EE 39 ADD HL,SP ; same index as specified +1176 06EF 7E LOKFOR: LD A,(HL) ; Get block ID +1177 06F0 23 INC HL ; Point to index address +1178 06F1 FE 81 CP ZFOR ; Is it a "FOR" token +1179 06F3 C0 RET NZ ; No - exit +1180 06F4 4E LD C,(HL) ; BC = Address of "FOR" index +1181 06F5 23 INC HL +1182 06F6 46 LD B,(HL) +1183 06F7 23 INC HL ; Point to sign of STEP +1184 06F8 E5 PUSH HL ; Save pointer to sign +1185 06F9 69 LD L,C ; HL = address of "FOR" index +1186 06FA 60 LD H,B +1187 06FB 7A LD A,D ; See if an index was specified +1188 06FC B3 OR E ; DE = 0 if no index specified +1189 06FD EB EX DE,HL ; Specified index into HL +1190 06FE CA 05 07 JP Z,INDFND ; Skip if no index given +1191 0701 EB EX DE,HL ; Index back into DE +1192 0702 CD 0A 0A CALL CPDEHL ; Compare index with one given +1193 0705 01 0D 00 INDFND: LD BC,16-3 ; Offset to next block +1194 0708 E1 POP HL ; Restore pointer to sign +1195 0709 C8 RET Z ; Return if block found +1196 070A 09 ADD HL,BC ; Point to next block +1197 070B C3 EF 06 JP LOKFOR ; Keep on looking +1198 070E +1199 070E CD 28 07 MOVUP: CALL ENFMEM ; See if enough memory +1200 0711 C5 MOVSTR: PUSH BC ; Save end of source +1201 0712 E3 EX (SP),HL ; Swap source and dest" end +1202 0713 C1 POP BC ; Get end of destination +1203 0714 CD 0A 0A MOVLP: CALL CPDEHL ; See if list moved +1204 0717 7E LD A,(HL) ; Get byte +1205 0718 02 LD (BC),A ; Move it +1206 0719 C8 RET Z ; Exit if all done +1207 071A 0B DEC BC ; Next byte to move to +1208 071B 2B DEC HL ; Next byte to move +1209 071C C3 14 07 JP MOVLP ; Loop until all bytes moved +1210 071F +1211 071F E5 CHKSTK: PUSH HL ; Save code string address +1212 0720 2A 8A 31 LD HL,(ARREND) ; Lowest free memory +1213 0723 06 00 LD B,0 ; BC = Number of levels to test +1214 0725 09 ADD HL,BC ; 2 Bytes for each level +1215 0726 09 ADD HL,BC +1216 0727 3E .BYTE 3EH ; Skip "PUSH HL" +1217 0728 E5 ENFMEM: PUSH HL ; Save code string address +1218 0729 3E D0 LD A,0D0H ;LOW -48 ; 48 Bytes minimum RAM +1219 072B 95 SUB L +1220 072C 6F LD L,A +1221 072D 3E FF LD A,0FFH; HIGH (-48) ; 48 Bytes minimum RAM +1222 072F 9C SBC A,H +1223 0730 DA 37 07 JP C,OMERR ; Not enough - ?OM Error +1224 0733 67 LD H,A +1225 0734 39 ADD HL,SP ; Test if stack is overflowed +1226 0735 E1 POP HL ; Restore code string address +1227 0736 D8 RET C ; Return if enough mmory +1228 0737 1E 0C OMERR: LD E,OM ; ?OM Error +1229 0739 C3 56 07 JP ERROR +1230 073C +1231 073C 2A 79 31 DATSNR: LD HL,(DATLIN) ; Get line of current DATA item +1232 073F 22 0C 31 LD (LINEAT),HL ; Save as current line +1233 0742 1E 02 SNERR: LD E,SN ; ?SN Error +1234 0744 01 .BYTE 01H ; Skip "LD E,DZ" +1235 0745 1E 14 DZERR: LD E,DZ ; ?/0 Error +1236 0747 01 .BYTE 01H ; Skip "LD E,NF" +1237 0748 1E 00 NFERR: LD E,NF ; ?NF Error +1238 074A 01 .BYTE 01H ; Skip "LD E,DD" +1239 074B 1E 12 DDERR: LD E,DD ; ?DD Error +1240 074D 01 .BYTE 01H ; Skip "LD E,UF" +1241 074E 1E 22 UFERR: LD E,UF ; ?UF Error +1242 0750 01 .BYTE 01H ; Skip "LD E,OV +1243 0751 1E 0A OVERR: LD E,OV ; ?OV Error +1244 0753 01 .BYTE 01H ; Skip "LD E,TM" +1245 0754 1E 18 TMERR: LD E,TM ; ?TM Error +1246 0756 +1247 0756 CD 74 08 ERROR: CALL CLREG ; Clear registers and stack +1248 0759 32 F5 30 LD (CTLOFG),A ; Enable output (A is 0) +1249 075C CD 35 0E CALL STTLIN ; Start new line +1250 075F 21 49 06 LD HL,ERRORS ; Point to error codes +1251 0762 57 LD D,A ; D = 0 (A is 0) +1252 0763 3E 3F LD A,'?' +1253 0765 CD 1B 0A CALL OUTC ; Output '?' +1254 0768 19 ADD HL,DE ; Offset to correct error code +1255 0769 7E LD A,(HL) ; First character +1256 076A CD 1B 0A CALL OUTC ; Output it +1257 076D CD 9A 0B CALL GETCHR ; Get next character +1258 0770 CD 1B 0A CALL OUTC ; Output it +1259 0773 21 D3 06 LD HL,ERRMSG ; "Error" message +1260 0776 CD E0 14 ERRIN: CALL PRS ; Output message +1261 0779 2A 0C 31 LD HL,(LINEAT) ; Get line of error +1262 077C 11 FE FF LD DE,-2 ; Cold start error if -2 +1263 077F CD 0A 0A CALL CPDEHL ; See if cold start error +1264 0782 CA A6 03 JP Z,CSTART ; Cold start error - Restart +1265 0785 7C LD A,H ; Was it a direct error? +1266 0786 A5 AND L ; Line = -1 if direct error +1267 0787 3C INC A +1268 0788 C4 7B 1B CALL NZ,LINEIN ; No - output line of error +1269 078B 3E .BYTE 3EH ; Skip "POP BC" +1270 078C C1 POPNOK: POP BC ; Drop address in input buffer +1271 078D +1272 078D AF PRNTOK: XOR A ; Output "Ok" and get command +1273 078E 32 F5 30 LD (CTLOFG),A ; Enable output +1274 0791 CD 35 0E CALL STTLIN ; Start new line +1275 0794 21 DF 06 LD HL,OKMSG ; "Ok" message +1276 0797 CD E0 14 CALL PRS ; Output "Ok" +1277 079A 21 FF FF GETCMD: LD HL,-1 ; Flag direct mode +1278 079D 22 0C 31 LD (LINEAT),HL ; Save as current line +1279 07A0 CD 87 09 CALL GETLIN ; Get an input line +1280 07A3 DA 9A 07 JP C,GETCMD ; Get line again if break +1281 07A6 CD 9A 0B CALL GETCHR ; Get first character +1282 07A9 3C INC A ; Test if end of line +1283 07AA 3D DEC A ; Without affecting Carry +1284 07AB CA 9A 07 JP Z,GETCMD ; Nothing entered - Get another +1285 07AE F5 PUSH AF ; Save Carry status +1286 07AF CD 66 0C CALL ATOH ; Get line number into DE +1287 07B2 D5 PUSH DE ; Save line number +1288 07B3 CD 9E 08 CALL CRUNCH ; Tokenise rest of line +1289 07B6 47 LD B,A ; Length of tokenised line +1290 07B7 D1 POP DE ; Restore line number +1291 07B8 F1 POP AF ; Restore Carry +1292 07B9 D2 7A 0B JP NC,EXCUTE ; No line number - Direct mode +1293 07BC D5 PUSH DE ; Save line number +1294 07BD C5 PUSH BC ; Save length of tokenised line +1295 07BE AF XOR A +1296 07BF 32 7C 31 LD (LSTBIN),A ; Clear last byte input +1297 07C2 CD 9A 0B CALL GETCHR ; Get next character +1298 07C5 B7 OR A ; Set flags +1299 07C6 F5 PUSH AF ; And save them +1300 07C7 CD 2E 08 CALL SRCHLN ; Search for line number in DE +1301 07CA DA D3 07 JP C,LINFND ; Jump if line found +1302 07CD F1 POP AF ; Get status +1303 07CE F5 PUSH AF ; And re-save +1304 07CF CA 07 0D JP Z,ULERR ; Nothing after number - Error +1305 07D2 B7 OR A ; Clear Carry +1306 07D3 C5 LINFND: PUSH BC ; Save address of line in prog +1307 07D4 D2 EA 07 JP NC,INEWLN ; Line not found - Insert new +1308 07D7 EB EX DE,HL ; Next line address in DE +1309 07D8 2A 86 31 LD HL,(PROGND) ; End of program +1310 07DB 1A SFTPRG: LD A,(DE) ; Shift rest of program down +1311 07DC 02 LD (BC),A +1312 07DD 03 INC BC ; Next destination +1313 07DE 13 INC DE ; Next source +1314 07DF CD 0A 0A CALL CPDEHL ; All done? +1315 07E2 C2 DB 07 JP NZ,SFTPRG ; More to do +1316 07E5 60 LD H,B ; HL - New end of program +1317 07E6 69 LD L,C +1318 07E7 22 86 31 LD (PROGND),HL ; Update end of program +1319 07EA +1320 07EA D1 INEWLN: POP DE ; Get address of line, +1321 07EB F1 POP AF ; Get status +1322 07EC CA 11 08 JP Z,SETPTR ; No text - Set up pointers +1323 07EF 2A 86 31 LD HL,(PROGND) ; Get end of program +1324 07F2 E3 EX (SP),HL ; Get length of input line +1325 07F3 C1 POP BC ; End of program to BC +1326 07F4 09 ADD HL,BC ; Find new end +1327 07F5 E5 PUSH HL ; Save new end +1328 07F6 CD 0E 07 CALL MOVUP ; Make space for line +1329 07F9 E1 POP HL ; Restore new end +1330 07FA 22 86 31 LD (PROGND),HL ; Update end of program pointer +1331 07FD EB EX DE,HL ; Get line to move up in HL +1332 07FE 74 LD (HL),H ; Save MSB +1333 07FF D1 POP DE ; Get new line number +1334 0800 23 INC HL ; Skip pointer +1335 0801 23 INC HL +1336 0802 73 LD (HL),E ; Save LSB of line number +1337 0803 23 INC HL +1338 0804 72 LD (HL),D ; Save MSB of line number +1339 0805 23 INC HL ; To first byte in line +1340 0806 11 11 31 LD DE,BUFFER ; Copy buffer to program +1341 0809 1A MOVBUF: LD A,(DE) ; Get source +1342 080A 77 LD (HL),A ; Save destinations +1343 080B 23 INC HL ; Next source +1344 080C 13 INC DE ; Next destination +1345 080D B7 OR A ; Done? +1346 080E C2 09 08 JP NZ,MOVBUF ; No - Repeat +1347 0811 CD 5A 08 SETPTR: CALL RUNFST ; Set line pointers +1348 0814 23 INC HL ; To LSB of pointer +1349 0815 EB EX DE,HL ; Address to DE +1350 0816 62 PTRLP: LD H,D ; Address to HL +1351 0817 6B LD L,E +1352 0818 7E LD A,(HL) ; Get LSB of pointer +1353 0819 23 INC HL ; To MSB of pointer +1354 081A B6 OR (HL) ; Compare with MSB pointer +1355 081B CA 9A 07 JP Z,GETCMD ; Get command line if end +1356 081E 23 INC HL ; To LSB of line number +1357 081F 23 INC HL ; Skip line number +1358 0820 23 INC HL ; Point to first byte in line +1359 0821 AF XOR A ; Looking for 00 byte +1360 0822 BE FNDEND: CP (HL) ; Found end of line? +1361 0823 23 INC HL ; Move to next byte +1362 0824 C2 22 08 JP NZ,FNDEND ; No - Keep looking +1363 0827 EB EX DE,HL ; Next line address to HL +1364 0828 73 LD (HL),E ; Save LSB of pointer +1365 0829 23 INC HL +1366 082A 72 LD (HL),D ; Save MSB of pointer +1367 082B C3 16 08 JP PTRLP ; Do next line +1368 082E +1369 082E 2A 0E 31 SRCHLN: LD HL,(BASTXT) ; Start of program text +1370 0831 44 SRCHLP: LD B,H ; BC = Address to look at +1371 0832 4D LD C,L +1372 0833 7E LD A,(HL) ; Get address of next line +1373 0834 23 INC HL +1374 0835 B6 OR (HL) ; End of program found? +1375 0836 2B DEC HL +1376 0837 C8 RET Z ; Yes - Line not found +1377 0838 23 INC HL +1378 0839 23 INC HL +1379 083A 7E LD A,(HL) ; Get LSB of line number +1380 083B 23 INC HL +1381 083C 66 LD H,(HL) ; Get MSB of line number +1382 083D 6F LD L,A +1383 083E CD 0A 0A CALL CPDEHL ; Compare with line in DE +1384 0841 60 LD H,B ; HL = Start of this line +1385 0842 69 LD L,C +1386 0843 7E LD A,(HL) ; Get LSB of next line address +1387 0844 23 INC HL +1388 0845 66 LD H,(HL) ; Get MSB of next line address +1389 0846 6F LD L,A ; Next line to HL +1390 0847 3F CCF +1391 0848 C8 RET Z ; Lines found - Exit +1392 0849 3F CCF +1393 084A D0 RET NC ; Line not found,at line after +1394 084B C3 31 08 JP SRCHLP ; Keep looking +1395 084E +1396 084E C0 NEW: RET NZ ; Return if any more on line +1397 084F 2A 0E 31 CLRPTR: LD HL,(BASTXT) ; Point to start of program +1398 0852 AF XOR A ; Set program area to empty +1399 0853 77 LD (HL),A ; Save LSB = 00 +1400 0854 23 INC HL +1401 0855 77 LD (HL),A ; Save MSB = 00 +1402 0856 23 INC HL +1403 0857 22 86 31 LD (PROGND),HL ; Set program end +1404 085A +1405 085A 2A 0E 31 RUNFST: LD HL,(BASTXT) ; Clear all variables +1406 085D 2B DEC HL +1407 085E +1408 085E 22 7E 31 INTVAR: LD (BRKLIN),HL ; Initialise RUN variables +1409 0861 2A 5F 31 LD HL,(LSTRAM) ; Get end of RAM +1410 0864 22 73 31 LD (STRBOT),HL ; Clear string space +1411 0867 AF XOR A +1412 0868 CD AA 0B CALL RESTOR ; Reset DATA pointers +1413 086B 2A 86 31 LD HL,(PROGND) ; Get end of program +1414 086E 22 88 31 LD (VAREND),HL ; Clear variables +1415 0871 22 8A 31 LD (ARREND),HL ; Clear arrays +1416 0874 +1417 0874 C1 CLREG: POP BC ; Save return address +1418 0875 2A 0A 31 LD HL,(STRSPC) ; Get end of working RAN +1419 0878 F9 LD SP,HL ; Set stack +1420 0879 21 63 31 LD HL,TMSTPL ; Temporary string pool +1421 087C 22 61 31 LD (TMSTPT),HL ; Reset temporary string ptr +1422 087F AF XOR A ; A = 00 +1423 0880 6F LD L,A ; HL = 0000 +1424 0881 67 LD H,A +1425 0882 22 84 31 LD (CONTAD),HL ; No CONTinue +1426 0885 32 7B 31 LD (FORFLG),A ; Clear FOR flag +1427 0888 22 8E 31 LD (FNRGNM),HL ; Clear FN argument +1428 088B E5 PUSH HL ; HL = 0000 +1429 088C C5 PUSH BC ; Put back return +1430 088D 2A 7E 31 DOAGN: LD HL,(BRKLIN) ; Get address of code to RUN +1431 0890 C9 RET ; Return to execution driver +1432 0891 +1433 0891 3E 3F PROMPT: LD A,'?' ; '?' +1434 0893 CD 1B 0A CALL OUTC ; Output character +1435 0896 3E 20 LD A,' ' ; Space +1436 0898 CD 1B 0A CALL OUTC ; Output character +1437 089B C3 FE 30 JP RINPUT ; Get input line +1438 089E +1439 089E AF CRUNCH: XOR A ; Tokenise line @ HL to BUFFER +1440 089F 32 5E 31 LD (DATFLG),A ; Reset literal flag +1441 08A2 0E 05 LD C,2+3 ; 2 byte number and 3 nulls +1442 08A4 11 11 31 LD DE,BUFFER ; Start of input buffer +1443 08A7 7E CRNCLP: LD A,(HL) ; Get byte +1444 08A8 FE 20 CP ' ' ; Is it a space? +1445 08AA CA 26 09 JP Z,MOVDIR ; Yes - Copy direct +1446 08AD 47 LD B,A ; Save character +1447 08AE FE 22 CP '"' ; Is it a quote? +1448 08B0 CA 46 09 JP Z,CPYLIT ; Yes - Copy literal string +1449 08B3 B7 OR A ; Is it end of buffer? +1450 08B4 CA 4D 09 JP Z,ENDBUF ; Yes - End buffer +1451 08B7 3A 5E 31 LD A,(DATFLG) ; Get data type +1452 08BA B7 OR A ; Literal? +1453 08BB 7E LD A,(HL) ; Get byte to copy +1454 08BC C2 26 09 JP NZ,MOVDIR ; Literal - Copy direct +1455 08BF FE 3F CP '?' ; Is it '?' short for PRINT +1456 08C1 3E 9E LD A,ZPRINT ; "PRINT" token +1457 08C3 CA 26 09 JP Z,MOVDIR ; Yes - replace it +1458 08C6 7E LD A,(HL) ; Get byte again +1459 08C7 FE 30 CP '0' ; Is it less than '0' +1460 08C9 DA D1 08 JP C,FNDWRD ; Yes - Look for reserved words +1461 08CC FE 3C CP 60; ";"+1 ; Is it "0123456789:;" ? +1462 08CE DA 26 09 JP C,MOVDIR ; Yes - copy it direct +1463 08D1 D5 FNDWRD: PUSH DE ; Look for reserved words +1464 08D2 11 CA 04 LD DE,WORDS-1 ; Point to table +1465 08D5 C5 PUSH BC ; Save count +1466 08D6 01 22 09 LD BC,RETNAD ; Where to return to +1467 08D9 C5 PUSH BC ; Save return address +1468 08DA 06 7F LD B,ZEND-1 ; First token value -1 +1469 08DC 7E LD A,(HL) ; Get byte +1470 08DD FE 61 CP 'a' ; Less than 'a' ? +1471 08DF DA EA 08 JP C,SEARCH ; Yes - search for words +1472 08E2 FE 7B CP 'z'+1 ; Greater than 'z' ? +1473 08E4 D2 EA 08 JP NC,SEARCH ; Yes - search for words +1474 08E7 E6 5F AND 01011111B ; Force upper case +1475 08E9 77 LD (HL),A ; Replace byte +1476 08EA 4E SEARCH: LD C,(HL) ; Search for a word +1477 08EB EB EX DE,HL +1478 08EC 23 GETNXT: INC HL ; Get next reserved word +1479 08ED B6 OR (HL) ; Start of word? +1480 08EE F2 EC 08 JP P,GETNXT ; No - move on +1481 08F1 04 INC B ; Increment token value +1482 08F2 7E LD A, (HL) ; Get byte from table +1483 08F3 E6 7F AND 01111111B ; Strip bit 7 +1484 08F5 C8 RET Z ; Return if end of list +1485 08F6 B9 CP C ; Same character as in buffer? +1486 08F7 C2 EC 08 JP NZ,GETNXT ; No - get next word +1487 08FA EB EX DE,HL +1488 08FB E5 PUSH HL ; Save start of word +1489 08FC +1490 08FC 13 NXTBYT: INC DE ; Look through rest of word +1491 08FD 1A LD A,(DE) ; Get byte from table +1492 08FE B7 OR A ; End of word ? +1493 08FF FA 1E 09 JP M,MATCH ; Yes - Match found +1494 0902 4F LD C,A ; Save it +1495 0903 78 LD A,B ; Get token value +1496 0904 FE 88 CP ZGOTO ; Is it "GOTO" token ? +1497 0906 C2 0D 09 JP NZ,NOSPC ; No - Don't allow spaces +1498 0909 CD 9A 0B CALL GETCHR ; Get next character +1499 090C 2B DEC HL ; Cancel increment from GETCHR +1500 090D 23 NOSPC: INC HL ; Next byte +1501 090E 7E LD A,(HL) ; Get byte +1502 090F FE 61 CP 'a' ; Less than 'a' ? +1503 0911 DA 16 09 JP C,NOCHNG ; Yes - don't change +1504 0914 E6 5F AND 01011111B ; Make upper case +1505 0916 B9 NOCHNG: CP C ; Same as in buffer ? +1506 0917 CA FC 08 JP Z,NXTBYT ; Yes - keep testing +1507 091A E1 POP HL ; Get back start of word +1508 091B C3 EA 08 JP SEARCH ; Look at next word +1509 091E +1510 091E 48 MATCH: LD C,B ; Word found - Save token value +1511 091F F1 POP AF ; Throw away return +1512 0920 EB EX DE,HL +1513 0921 C9 RET ; Return to "RETNAD" +1514 0922 EB RETNAD: EX DE,HL ; Get address in string +1515 0923 79 LD A,C ; Get token value +1516 0924 C1 POP BC ; Restore buffer length +1517 0925 D1 POP DE ; Get destination address +1518 0926 23 MOVDIR: INC HL ; Next source in buffer +1519 0927 12 LD (DE),A ; Put byte in buffer +1520 0928 13 INC DE ; Move up buffer +1521 0929 0C INC C ; Increment length of buffer +1522 092A D6 3A SUB ':' ; End of statement? +1523 092C CA 34 09 JP Z,SETLIT ; Jump if multi-statement line +1524 092F FE 49 CP ZDATA-3AH ; Is it DATA statement ? +1525 0931 C2 37 09 JP NZ,TSTREM ; No - see if REM +1526 0934 32 5E 31 SETLIT: LD (DATFLG),A ; Set literal flag +1527 0937 D6 54 TSTREM: SUB ZREM-3AH ; Is it REM? +1528 0939 C2 A7 08 JP NZ,CRNCLP ; No - Leave flag +1529 093C 47 LD B,A ; Copy rest of buffer +1530 093D 7E NXTCHR: LD A,(HL) ; Get byte +1531 093E B7 OR A ; End of line ? +1532 093F CA 4D 09 JP Z,ENDBUF ; Yes - Terminate buffer +1533 0942 B8 CP B ; End of statement ? +1534 0943 CA 26 09 JP Z,MOVDIR ; Yes - Get next one +1535 0946 23 CPYLIT: INC HL ; Move up source string +1536 0947 12 LD (DE),A ; Save in destination +1537 0948 0C INC C ; Increment length +1538 0949 13 INC DE ; Move up destination +1539 094A C3 3D 09 JP NXTCHR ; Repeat +1540 094D +1541 094D 21 10 31 ENDBUF: LD HL,BUFFER-1 ; Point to start of buffer +1542 0950 12 LD (DE),A ; Mark end of buffer (A = 00) +1543 0951 13 INC DE +1544 0952 12 LD (DE),A ; A = 00 +1545 0953 13 INC DE +1546 0954 12 LD (DE),A ; A = 00 +1547 0955 C9 RET +1548 0956 +1549 0956 3A F4 30 DODEL: LD A,(NULFLG) ; Get null flag status +1550 0959 B7 OR A ; Is it zero? +1551 095A 3E 00 LD A,0 ; Zero A - Leave flags +1552 095C 32 F4 30 LD (NULFLG),A ; Zero null flag +1553 095F C2 6A 09 JP NZ,ECHDEL ; Set - Echo it +1554 0962 05 DEC B ; Decrement length +1555 0963 CA 87 09 JP Z,GETLIN ; Get line again if empty +1556 0966 CD 1B 0A CALL OUTC ; Output null character +1557 0969 3E .BYTE 3EH ; Skip "DEC B" +1558 096A 05 ECHDEL: DEC B ; Count bytes in buffer +1559 096B 2B DEC HL ; Back space buffer +1560 096C CA 7E 09 JP Z,OTKLN ; No buffer - Try again +1561 096F 7E LD A,(HL) ; Get deleted byte +1562 0970 CD 1B 0A CALL OUTC ; Echo it +1563 0973 C3 90 09 JP MORINP ; Get more input +1564 0976 +1565 0976 05 DELCHR: DEC B ; Count bytes in buffer +1566 0977 2B DEC HL ; Back space buffer +1567 0978 CD 1B 0A CALL OUTC ; Output character in A +1568 097B C2 90 09 JP NZ,MORINP ; Not end - Get more +1569 097E CD 1B 0A OTKLN: CALL OUTC ; Output character in A +1570 0981 CD 42 0E KILIN: CALL PRNTCRLF ; Output CRLF +1571 0984 C3 87 09 JP TTYLIN ; Get line again +1572 0987 +1573 0987 GETLIN: +1574 0987 21 11 31 TTYLIN: LD HL,BUFFER ; Get a line by character +1575 098A 06 01 LD B,1 ; Set buffer as empty +1576 098C AF XOR A +1577 098D 32 F4 30 LD (NULFLG),A ; Clear null flag +1578 0990 CD 45 0A MORINP: CALL CLOTST ; Get character and test ^O +1579 0993 4F LD C,A ; Save character in C +1580 0994 FE 7F CP DEL ; Delete character? +1581 0996 CA 56 09 JP Z,DODEL ; Yes - Process it +1582 0999 3A F4 30 LD A,(NULFLG) ; Get null flag +1583 099C B7 OR A ; Test null flag status +1584 099D CA A9 09 JP Z,PROCES ; Reset - Process character +1585 09A0 3E 00 LD A,0 ; Set a null +1586 09A2 CD 1B 0A CALL OUTC ; Output null +1587 09A5 AF XOR A ; Clear A +1588 09A6 32 F4 30 LD (NULFLG),A ; Reset null flag +1589 09A9 79 PROCES: LD A,C ; Get character +1590 09AA FE 07 CP CTRLG ; Bell? +1591 09AC CA ED 09 JP Z,PUTCTL ; Yes - Save it +1592 09AF FE 03 CP CTRLC ; Is it control "C"? +1593 09B1 CC 42 0E CALL Z,PRNTCRLF ; Yes - Output CRLF +1594 09B4 37 SCF ; Flag break +1595 09B5 C8 RET Z ; Return if control "C" +1596 09B6 FE 0D CP CR ; Is it enter? +1597 09B8 CA 3D 0E JP Z,ENDINP ; Yes - Terminate input +1598 09BB FE 15 CP CTRLU ; Is it control "U"? +1599 09BD CA 81 09 JP Z,KILIN ; Yes - Get another line +1600 09C0 FE 40 CP '@' ; Is it "kill line"? +1601 09C2 CA 7E 09 JP Z,OTKLN ; Yes - Kill line +1602 09C5 FE 5F CP '_' ; Is it delete? +1603 09C7 CA 76 09 JP Z,DELCHR ; Yes - Delete character +1604 09CA FE 08 CP BKSP ; Is it backspace? +1605 09CC CA 76 09 JP Z,DELCHR ; Yes - Delete character +1606 09CF FE 12 CP CTRLR ; Is it control "R"? +1607 09D1 C2 E8 09 JP NZ,PUTBUF ; No - Put in buffer +1608 09D4 C5 PUSH BC ; Save buffer length +1609 09D5 D5 PUSH DE ; Save DE +1610 09D6 E5 PUSH HL ; Save buffer address +1611 09D7 36 00 LD (HL),0 ; Mark end of buffer +1612 09D9 CD F2 1F CALL OUTNCR ; Output and do CRLF +1613 09DC 21 11 31 LD HL,BUFFER ; Point to buffer start +1614 09DF CD E0 14 CALL PRS ; Output buffer +1615 09E2 E1 POP HL ; Restore buffer address +1616 09E3 D1 POP DE ; Restore DE +1617 09E4 C1 POP BC ; Restore buffer length +1618 09E5 C3 90 09 JP MORINP ; Get another character +1619 09E8 +1620 09E8 FE 20 PUTBUF: CP ' ' ; Is it a control code? +1621 09EA DA 90 09 JP C,MORINP ; Yes - Ignore +1622 09ED 78 PUTCTL: LD A,B ; Get number of bytes in buffer +1623 09EE FE 49 CP 72+1 ; Test for line overflow +1624 09F0 3E 07 LD A,CTRLG ; Set a bell +1625 09F2 D2 02 0A JP NC,OUTNBS ; Ring bell if buffer full +1626 09F5 79 LD A,C ; Get character +1627 09F6 71 LD (HL),C ; Save in buffer +1628 09F7 32 7C 31 LD (LSTBIN),A ; Save last input byte +1629 09FA 23 INC HL ; Move up buffer +1630 09FB 04 INC B ; Increment length +1631 09FC CD 1B 0A OUTIT: CALL OUTC ; Output the character entered +1632 09FF C3 90 09 JP MORINP ; Get another character +1633 0A02 +1634 0A02 CD 1B 0A OUTNBS: CALL OUTC ; Output bell and back over it +1635 0A05 3E 08 LD A,BKSP ; Set back space +1636 0A07 C3 FC 09 JP OUTIT ; Output it and get more +1637 0A0A +1638 0A0A 7C CPDEHL: LD A,H ; Get H +1639 0A0B 92 SUB D ; Compare with D +1640 0A0C C0 RET NZ ; Different - Exit +1641 0A0D 7D LD A,L ; Get L +1642 0A0E 93 SUB E ; Compare with E +1643 0A0F C9 RET ; Return status +1644 0A10 +1645 0A10 7E CHKSYN: LD A,(HL) ; Check syntax of character +1646 0A11 E3 EX (SP),HL ; Address of test byte +1647 0A12 BE CP (HL) ; Same as in code string? +1648 0A13 23 INC HL ; Return address +1649 0A14 E3 EX (SP),HL ; Put it back +1650 0A15 CA 9A 0B JP Z,GETCHR ; Yes - Get next character +1651 0A18 C3 42 07 JP SNERR ; Different - ?SN Error +1652 0A1B +1653 0A1B F5 OUTC: PUSH AF ; Save character +1654 0A1C 3A F5 30 LD A,(CTLOFG) ; Get control "O" flag +1655 0A1F B7 OR A ; Is it set? +1656 0A20 C2 15 15 JP NZ,POPAF ; Yes - don't output +1657 0A23 F1 POP AF ; Restore character +1658 0A24 C5 PUSH BC ; Save buffer length +1659 0A25 F5 PUSH AF ; Save character +1660 0A26 FE 20 CP ' ' ; Is it a control code? +1661 0A28 DA 3F 0A JP C,DINPOS ; Yes - Don't INC POS(X) +1662 0A2B 3A F2 30 LD A,(LWIDTH) ; Get line width +1663 0A2E 47 LD B,A ; To B +1664 0A2F 3A 5B 31 LD A,(CURPOS) ; Get cursor position +1665 0A32 04 INC B ; Width 255? +1666 0A33 CA 3B 0A JP Z,INCLEN ; Yes - No width limit +1667 0A36 05 DEC B ; Restore width +1668 0A37 B8 CP B ; At end of line? +1669 0A38 CC 42 0E CALL Z,PRNTCRLF ; Yes - output CRLF +1670 0A3B 3C INCLEN: INC A ; Move on one character +1671 0A3C 32 5B 31 LD (CURPOS),A ; Save new position +1672 0A3F F1 DINPOS: POP AF ; Restore character +1673 0A40 C1 POP BC ; Restore buffer length +1674 0A41 CD DB 1F CALL MONOUT ; Send it +1675 0A44 C9 RET +1676 0A45 +1677 0A45 CD 9F 1E CLOTST: CALL GETINP ; Get input character +1678 0A48 E6 7F AND 01111111B ; Strip bit 7 +1679 0A4A FE 0F CP CTRLO ; Is it control "O"? +1680 0A4C C0 RET NZ ; No don't flip flag +1681 0A4D 3A F5 30 LD A,(CTLOFG) ; Get flag +1682 0A50 2F CPL ; Flip it +1683 0A51 32 F5 30 LD (CTLOFG),A ; Put it back +1684 0A54 AF XOR A ; Null character +1685 0A55 C9 RET +1686 0A56 +1687 0A56 CD 66 0C LIST: CALL ATOH ; ASCII number to DE +1688 0A59 C0 RET NZ ; Return if anything extra +1689 0A5A C1 POP BC ; Rubbish - Not needed +1690 0A5B CD 2E 08 CALL SRCHLN ; Search for line number in DE +1691 0A5E C5 PUSH BC ; Save address of line +1692 0A5F CD AC 0A CALL SETLIN ; Set up lines counter +1693 0A62 E1 LISTLP: POP HL ; Restore address of line +1694 0A63 4E LD C,(HL) ; Get LSB of next line +1695 0A64 23 INC HL +1696 0A65 46 LD B,(HL) ; Get MSB of next line +1697 0A66 23 INC HL +1698 0A67 78 LD A,B ; BC = 0 (End of program)? +1699 0A68 B1 OR C +1700 0A69 CA 8D 07 JP Z,PRNTOK ; Yes - Go to command mode +1701 0A6C CD B5 0A CALL COUNT ; Count lines +1702 0A6F CD C5 0B CALL TSTBRK ; Test for break key +1703 0A72 C5 PUSH BC ; Save address of next line +1704 0A73 CD 42 0E CALL PRNTCRLF ; Output CRLF +1705 0A76 5E LD E,(HL) ; Get LSB of line number +1706 0A77 23 INC HL +1707 0A78 56 LD D,(HL) ; Get MSB of line number +1708 0A79 23 INC HL +1709 0A7A E5 PUSH HL ; Save address of line start +1710 0A7B EB EX DE,HL ; Line number to HL +1711 0A7C CD 83 1B CALL PRNTHL ; Output line number in decimal +1712 0A7F 3E 20 LD A,' ' ; Space after line number +1713 0A81 E1 POP HL ; Restore start of line address +1714 0A82 CD 1B 0A LSTLP2: CALL OUTC ; Output character in A +1715 0A85 7E LSTLP3: LD A,(HL) ; Get next byte in line +1716 0A86 B7 OR A ; End of line? +1717 0A87 23 INC HL ; To next byte in line +1718 0A88 CA 62 0A JP Z,LISTLP ; Yes - get next line +1719 0A8B F2 82 0A JP P,LSTLP2 ; No token - output it +1720 0A8E D6 7F SUB ZEND-1 ; Find and output word +1721 0A90 4F LD C,A ; Token offset+1 to C +1722 0A91 11 CB 04 LD DE,WORDS ; Reserved word list +1723 0A94 1A FNDTOK: LD A,(DE) ; Get character in list +1724 0A95 13 INC DE ; Move on to next +1725 0A96 B7 OR A ; Is it start of word? +1726 0A97 F2 94 0A JP P,FNDTOK ; No - Keep looking for word +1727 0A9A 0D DEC C ; Count words +1728 0A9B C2 94 0A JP NZ,FNDTOK ; Not there - keep looking +1729 0A9E E6 7F OUTWRD: AND 01111111B ; Strip bit 7 +1730 0AA0 CD 1B 0A CALL OUTC ; Output first character +1731 0AA3 1A LD A,(DE) ; Get next character +1732 0AA4 13 INC DE ; Move on to next +1733 0AA5 B7 OR A ; Is it end of word? +1734 0AA6 F2 9E 0A JP P,OUTWRD ; No - output the rest +1735 0AA9 C3 85 0A JP LSTLP3 ; Next byte in line +1736 0AAC +1737 0AAC E5 SETLIN: PUSH HL ; Set up LINES counter +1738 0AAD 2A F8 30 LD HL,(LINESN) ; Get LINES number +1739 0AB0 22 F6 30 LD (LINESC),HL ; Save in LINES counter +1740 0AB3 E1 POP HL +1741 0AB4 C9 RET +1742 0AB5 +1743 0AB5 E5 COUNT: PUSH HL ; Save code string address +1744 0AB6 D5 PUSH DE +1745 0AB7 2A F6 30 LD HL,(LINESC) ; Get LINES counter +1746 0ABA 11 FF FF LD DE,-1 +1747 0ABD ED 5A ADC HL,DE ; Decrement +1748 0ABF 22 F6 30 LD (LINESC),HL ; Put it back +1749 0AC2 D1 POP DE +1750 0AC3 E1 POP HL ; Restore code string address +1751 0AC4 F0 RET P ; Return if more lines to go +1752 0AC5 E5 PUSH HL ; Save code string address +1753 0AC6 2A F8 30 LD HL,(LINESN) ; Get LINES number +1754 0AC9 22 F6 30 LD (LINESC),HL ; Reset LINES counter +1755 0ACC CD 9F 1E CALL GETINP ; Get input character +1756 0ACF FE 03 CP CTRLC ; Is it control "C"? +1757 0AD1 CA D8 0A JP Z,RSLNBK ; Yes - Reset LINES and break +1758 0AD4 E1 POP HL ; Restore code string address +1759 0AD5 C3 B5 0A JP COUNT ; Keep on counting +1760 0AD8 +1761 0AD8 2A F8 30 RSLNBK: LD HL,(LINESN) ; Get LINES number +1762 0ADB 22 F6 30 LD (LINESC),HL ; Reset LINES counter +1763 0ADE C3 3C 04 JP BRKRET ; Go and output "Break" +1764 0AE1 +1765 0AE1 3E 64 FOR: LD A,64H ; Flag "FOR" assignment +1766 0AE3 32 7B 31 LD (FORFLG),A ; Save "FOR" flag +1767 0AE6 CD 48 0D CALL LET ; Set up initial index +1768 0AE9 C1 POP BC ; Drop RETurn address +1769 0AEA E5 PUSH HL ; Save code string address +1770 0AEB CD 31 0D CALL DATA ; Get next statement address +1771 0AEE 22 77 31 LD (LOOPST),HL ; Save it for start of loop +1772 0AF1 21 02 00 LD HL,2 ; Offset for "FOR" block +1773 0AF4 39 ADD HL,SP ; Point to it +1774 0AF5 CD EF 06 FORSLP: CALL LOKFOR ; Look for existing "FOR" block +1775 0AF8 D1 POP DE ; Get code string address +1776 0AF9 C2 11 0B JP NZ,FORFND ; No nesting found +1777 0AFC 09 ADD HL,BC ; Move into "FOR" block +1778 0AFD D5 PUSH DE ; Save code string address +1779 0AFE 2B DEC HL +1780 0AFF 56 LD D,(HL) ; Get MSB of loop statement +1781 0B00 2B DEC HL +1782 0B01 5E LD E,(HL) ; Get LSB of loop statement +1783 0B02 23 INC HL +1784 0B03 23 INC HL +1785 0B04 E5 PUSH HL ; Save block address +1786 0B05 2A 77 31 LD HL,(LOOPST) ; Get address of loop statement +1787 0B08 CD 0A 0A CALL CPDEHL ; Compare the FOR loops +1788 0B0B E1 POP HL ; Restore block address +1789 0B0C C2 F5 0A JP NZ,FORSLP ; Different FORs - Find another +1790 0B0F D1 POP DE ; Restore code string address +1791 0B10 F9 LD SP,HL ; Remove all nested loops +1792 0B11 +1793 0B11 EB FORFND: EX DE,HL ; Code string address to HL +1794 0B12 0E 08 LD C,8 +1795 0B14 CD 1F 07 CALL CHKSTK ; Check for 8 levels of stack +1796 0B17 E5 PUSH HL ; Save code string address +1797 0B18 2A 77 31 LD HL,(LOOPST) ; Get first statement of loop +1798 0B1B E3 EX (SP),HL ; Save and restore code string +1799 0B1C E5 PUSH HL ; Re-save code string address +1800 0B1D 2A 0C 31 LD HL,(LINEAT) ; Get current line number +1801 0B20 E3 EX (SP),HL ; Save and restore code string +1802 0B21 CD 0A 10 CALL TSTNUM ; Make sure it's a number +1803 0B24 CD 10 0A CALL CHKSYN ; Make sure "TO" is next +1804 0B27 A6 .BYTE ZTO ; "TO" token +1805 0B28 CD 07 10 CALL GETNUM ; Get "TO" expression value +1806 0B2B E5 PUSH HL ; Save code string address +1807 0B2C CD 35 1A CALL BCDEFP ; Move "TO" value to BCDE +1808 0B2F E1 POP HL ; Restore code string address +1809 0B30 C5 PUSH BC ; Save "TO" value in block +1810 0B31 D5 PUSH DE +1811 0B32 01 00 81 LD BC,8100H ; BCDE - 1 (default STEP) +1812 0B35 51 LD D,C ; C=0 +1813 0B36 5A LD E,D ; D=0 +1814 0B37 7E LD A,(HL) ; Get next byte in code string +1815 0B38 FE AB CP ZSTEP ; See if "STEP" is stated +1816 0B3A 3E 01 LD A,1 ; Sign of step = 1 +1817 0B3C C2 4D 0B JP NZ,SAVSTP ; No STEP given - Default to 1 +1818 0B3F CD 9A 0B CALL GETCHR ; Jump over "STEP" token +1819 0B42 CD 07 10 CALL GETNUM ; Get step value +1820 0B45 E5 PUSH HL ; Save code string address +1821 0B46 CD 35 1A CALL BCDEFP ; Move STEP to BCDE +1822 0B49 CD E9 19 CALL TSTSGN ; Test sign of FPREG +1823 0B4C E1 POP HL ; Restore code string address +1824 0B4D C5 SAVSTP: PUSH BC ; Save the STEP value in block +1825 0B4E D5 PUSH DE +1826 0B4F F5 PUSH AF ; Save sign of STEP +1827 0B50 33 INC SP ; Don't save flags +1828 0B51 E5 PUSH HL ; Save code string address +1829 0B52 2A 7E 31 LD HL,(BRKLIN) ; Get address of index variable +1830 0B55 E3 EX (SP),HL ; Save and restore code string +1831 0B56 06 81 PUTFID: LD B,ZFOR ; "FOR" block marker +1832 0B58 C5 PUSH BC ; Save it +1833 0B59 33 INC SP ; Don't save C +1834 0B5A +1835 0B5A CD C5 0B RUNCNT: CALL TSTBRK ; Execution driver - Test break +1836 0B5D 22 7E 31 LD (BRKLIN),HL ; Save code address for break +1837 0B60 7E LD A,(HL) ; Get next byte in code string +1838 0B61 FE 3A CP ':' ; Multi statement line? +1839 0B63 CA 7A 0B JP Z,EXCUTE ; Yes - Execute it +1840 0B66 B7 OR A ; End of line? +1841 0B67 C2 42 07 JP NZ,SNERR ; No - Syntax error +1842 0B6A 23 INC HL ; Point to address of next line +1843 0B6B 7E LD A,(HL) ; Get LSB of line pointer +1844 0B6C 23 INC HL +1845 0B6D B6 OR (HL) ; Is it zero (End of prog)? +1846 0B6E CA EC 0B JP Z,ENDPRG ; Yes - Terminate execution +1847 0B71 23 INC HL ; Point to line number +1848 0B72 5E LD E,(HL) ; Get LSB of line number +1849 0B73 23 INC HL +1850 0B74 56 LD D,(HL) ; Get MSB of line number +1851 0B75 EB EX DE,HL ; Line number to HL +1852 0B76 22 0C 31 LD (LINEAT),HL ; Save as current line number +1853 0B79 EB EX DE,HL ; Line number back to DE +1854 0B7A CD 9A 0B EXCUTE: CALL GETCHR ; Get key word +1855 0B7D 11 5A 0B LD DE,RUNCNT ; Where to RETurn to +1856 0B80 D5 PUSH DE ; Save for RETurn +1857 0B81 C8 IFJMP: RET Z ; Go to RUNCNT if end of STMT +1858 0B82 D6 80 ONJMP: SUB ZEND ; Is it a token? +1859 0B84 DA 48 0D JP C,LET ; No - try to assign it +1860 0B87 FE 25 CP ZNEW+1-ZEND ; END to NEW ? +1861 0B89 D2 42 07 JP NC,SNERR ; Not a key word - ?SN Error +1862 0B8C 07 RLCA ; Double it +1863 0B8D 4F LD C,A ; BC = Offset into table +1864 0B8E 06 00 LD B,0 +1865 0B90 EB EX DE,HL ; Save code string address +1866 0B91 21 EA 05 LD HL,WORDTB ; Keyword address table +1867 0B94 09 ADD HL,BC ; Point to routine address +1868 0B95 4E LD C,(HL) ; Get LSB of routine address +1869 0B96 23 INC HL +1870 0B97 46 LD B,(HL) ; Get MSB of routine address +1871 0B98 C5 PUSH BC ; Save routine address +1872 0B99 EB EX DE,HL ; Restore code string address +1873 0B9A +1874 0B9A 23 GETCHR: INC HL ; Point to next character +1875 0B9B 7E LD A,(HL) ; Get next code string byte +1876 0B9C FE 3A CP ':' ; Z if ':' +1877 0B9E D0 RET NC ; NC if > "9" +1878 0B9F FE 20 CP ' ' +1879 0BA1 CA 9A 0B JP Z,GETCHR ; Skip over spaces +1880 0BA4 FE 30 CP '0' +1881 0BA6 3F CCF ; NC if < '0' +1882 0BA7 3C INC A ; Test for zero - Leave carry +1883 0BA8 3D DEC A ; Z if Null +1884 0BA9 C9 RET +1885 0BAA +1886 0BAA EB RESTOR: EX DE,HL ; Save code string address +1887 0BAB 2A 0E 31 LD HL,(BASTXT) ; Point to start of program +1888 0BAE CA BF 0B JP Z,RESTNL ; Just RESTORE - reset pointer +1889 0BB1 EB EX DE,HL ; Restore code string address +1890 0BB2 CD 66 0C CALL ATOH ; Get line number to DE +1891 0BB5 E5 PUSH HL ; Save code string address +1892 0BB6 CD 2E 08 CALL SRCHLN ; Search for line number in DE +1893 0BB9 60 LD H,B ; HL = Address of line +1894 0BBA 69 LD L,C +1895 0BBB D1 POP DE ; Restore code string address +1896 0BBC D2 07 0D JP NC,ULERR ; ?UL Error if not found +1897 0BBF 2B RESTNL: DEC HL ; Byte before DATA statement +1898 0BC0 22 8C 31 UPDATA: LD (NXTDAT),HL ; Update DATA pointer +1899 0BC3 EB EX DE,HL ; Restore code string address +1900 0BC4 C9 RET +1901 0BC5 +1902 0BC5 +1903 0BC5 DF TSTBRK: RST 18H ; Check input status +1904 0BC6 C8 RET Z ; No key, go back +1905 0BC7 D7 RST 10H ; Get the key into A +1906 0BC8 FE 1B CP ESC ; Escape key? +1907 0BCA 28 11 JR Z,BRK ; Yes, break +1908 0BCC FE 03 CP CTRLC ; +1909 0BCE 28 0D JR Z,BRK ; Yes, break +1910 0BD0 FE 13 CP CTRLS ; Stop scrolling? +1911 0BD2 C0 RET NZ ; Other key, ignore +1912 0BD3 +1913 0BD3 +1914 0BD3 D7 STALL: RST 10H ; Wait for key +1915 0BD4 FE 11 CP CTRLQ ; Resume scrolling? +1916 0BD6 C8 RET Z ; Release the chokehold +1917 0BD7 FE 03 CP CTRLC ; Second break? +1918 0BD9 28 07 JR Z,STOP ; Break during hold exits prog +1919 0BDB 18 F6 JR STALL ; Loop until or +1920 0BDD +1921 0BDD 3E FF BRK LD A,$FF ; Set BRKFLG +1922 0BDF 32 FD 30 LD (BRKFLG),A ; Store it +1923 0BE2 +1924 0BE2 +1925 0BE2 C0 STOP: RET NZ ; Exit if anything else +1926 0BE3 F6 .BYTE 0F6H ; Flag "STOP" +1927 0BE4 C0 PEND: RET NZ ; Exit if anything else +1928 0BE5 22 7E 31 LD (BRKLIN),HL ; Save point of break +1929 0BE8 21 .BYTE 21H ; Skip "OR 11111111B" +1930 0BE9 F6 FF INPBRK: OR 11111111B ; Flag "Break" wanted +1931 0BEB C1 POP BC ; Return not needed and more +1932 0BEC 2A 0C 31 ENDPRG: LD HL,(LINEAT) ; Get current line number +1933 0BEF F5 PUSH AF ; Save STOP / END status +1934 0BF0 7D LD A,L ; Is it direct break? +1935 0BF1 A4 AND H +1936 0BF2 3C INC A ; Line is -1 if direct break +1937 0BF3 CA FF 0B JP Z,NOLIN ; Yes - No line number +1938 0BF6 22 82 31 LD (ERRLIN),HL ; Save line of break +1939 0BF9 2A 7E 31 LD HL,(BRKLIN) ; Get point of break +1940 0BFC 22 84 31 LD (CONTAD),HL ; Save point to CONTinue +1941 0BFF AF NOLIN: XOR A +1942 0C00 32 F5 30 LD (CTLOFG),A ; Enable output +1943 0C03 CD 35 0E CALL STTLIN ; Start a new line +1944 0C06 F1 POP AF ; Restore STOP / END status +1945 0C07 21 E5 06 LD HL,BRKMSG ; "Break" message +1946 0C0A C2 76 07 JP NZ,ERRIN ; "in line" wanted? +1947 0C0D C3 8D 07 JP PRNTOK ; Go to command mode +1948 0C10 +1949 0C10 2A 84 31 CONT: LD HL,(CONTAD) ; Get CONTinue address +1950 0C13 7C LD A,H ; Is it zero? +1951 0C14 B5 OR L +1952 0C15 1E 20 LD E,CN ; ?CN Error +1953 0C17 CA 56 07 JP Z,ERROR ; Yes - output "?CN Error" +1954 0C1A EB EX DE,HL ; Save code string address +1955 0C1B 2A 82 31 LD HL,(ERRLIN) ; Get line of last break +1956 0C1E 22 0C 31 LD (LINEAT),HL ; Set up current line number +1957 0C21 EB EX DE,HL ; Restore code string address +1958 0C22 C9 RET ; CONTinue where left off +1959 0C23 +1960 0C23 CD 68 17 NULL: CALL GETINT ; Get integer 0-255 +1961 0C26 C0 RET NZ ; Return if bad value +1962 0C27 32 F1 30 LD (NULLS),A ; Set nulls number +1963 0C2A C9 RET +1964 0C2B +1965 0C2B +1966 0C2B E5 ACCSUM: PUSH HL ; Save address in array +1967 0C2C 2A FA 30 LD HL,(CHKSUM) ; Get check sum +1968 0C2F 06 00 LD B,0 ; BC - Value of byte +1969 0C31 4F LD C,A +1970 0C32 09 ADD HL,BC ; Add byte to check sum +1971 0C33 22 FA 30 LD (CHKSUM),HL ; Re-save check sum +1972 0C36 E1 POP HL ; Restore address in array +1973 0C37 C9 RET +1974 0C38 +1975 0C38 7E CHKLTR: LD A,(HL) ; Get byte +1976 0C39 FE 41 CP 'A' ; < 'a' ? +1977 0C3B D8 RET C ; Carry set if not letter +1978 0C3C FE 5B CP 'Z'+1 ; > 'z' ? +1979 0C3E 3F CCF +1980 0C3F C9 RET ; Carry set if not letter +1981 0C40 +1982 0C40 CD 9A 0B FPSINT: CALL GETCHR ; Get next character +1983 0C43 CD 07 10 POSINT: CALL GETNUM ; Get integer 0 to 32767 +1984 0C46 CD E9 19 DEPINT: CALL TSTSGN ; Test sign of FPREG +1985 0C49 FA 61 0C JP M,FCERR ; Negative - ?FC Error +1986 0C4C 3A 97 31 DEINT: LD A,(FPEXP) ; Get integer value to DE +1987 0C4F FE 90 CP 80H+16 ; Exponent in range (16 bits)? +1988 0C51 DA 91 1A JP C,FPINT ; Yes - convert it +1989 0C54 01 80 90 LD BC,9080H ; BCDE = -32768 +1990 0C57 11 00 00 LD DE,0000 +1991 0C5A E5 PUSH HL ; Save code string address +1992 0C5B CD 64 1A CALL CMPNUM ; Compare FPREG with BCDE +1993 0C5E E1 POP HL ; Restore code string address +1994 0C5F 51 LD D,C ; MSB to D +1995 0C60 C8 RET Z ; Return if in range +1996 0C61 1E 08 FCERR: LD E,FC ; ?FC Error +1997 0C63 C3 56 07 JP ERROR ; Output error- +1998 0C66 +1999 0C66 2B ATOH: DEC HL ; ASCII number to DE binary +2000 0C67 11 00 00 GETLN: LD DE,0 ; Get number to DE +2001 0C6A CD 9A 0B GTLNLP: CALL GETCHR ; Get next character +2002 0C6D D0 RET NC ; Exit if not a digit +2003 0C6E E5 PUSH HL ; Save code string address +2004 0C6F F5 PUSH AF ; Save digit +2005 0C70 21 98 19 LD HL,65529/10 ; Largest number 65529 +2006 0C73 CD 0A 0A CALL CPDEHL ; Number in range? +2007 0C76 DA 42 07 JP C,SNERR ; No - ?SN Error +2008 0C79 62 LD H,D ; HL = Number +2009 0C7A 6B LD L,E +2010 0C7B 19 ADD HL,DE ; Times 2 +2011 0C7C 29 ADD HL,HL ; Times 4 +2012 0C7D 19 ADD HL,DE ; Times 5 +2013 0C7E 29 ADD HL,HL ; Times 10 +2014 0C7F F1 POP AF ; Restore digit +2015 0C80 D6 30 SUB '0' ; Make it 0 to 9 +2016 0C82 5F LD E,A ; DE = Value of digit +2017 0C83 16 00 LD D,0 +2018 0C85 19 ADD HL,DE ; Add to number +2019 0C86 EB EX DE,HL ; Number to DE +2020 0C87 E1 POP HL ; Restore code string address +2021 0C88 C3 6A 0C JP GTLNLP ; Go to next character +2022 0C8B +2023 0C8B CA 5E 08 CLEAR: JP Z,INTVAR ; Just "CLEAR" Keep parameters +2024 0C8E CD 43 0C CALL POSINT ; Get integer 0 to 32767 to DE +2025 0C91 2B DEC HL ; Cancel increment +2026 0C92 CD 9A 0B CALL GETCHR ; Get next character +2027 0C95 E5 PUSH HL ; Save code string address +2028 0C96 2A 5F 31 LD HL,(LSTRAM) ; Get end of RAM +2029 0C99 CA AE 0C JP Z,STORED ; No value given - Use stored +2030 0C9C E1 POP HL ; Restore code string address +2031 0C9D CD 10 0A CALL CHKSYN ; Check for comma +2032 0CA0 2C .BYTE ',' +2033 0CA1 D5 PUSH DE ; Save number +2034 0CA2 CD 43 0C CALL POSINT ; Get integer 0 to 32767 +2035 0CA5 2B DEC HL ; Cancel increment +2036 0CA6 CD 9A 0B CALL GETCHR ; Get next character +2037 0CA9 C2 42 07 JP NZ,SNERR ; ?SN Error if more on line +2038 0CAC E3 EX (SP),HL ; Save code string address +2039 0CAD EB EX DE,HL ; Number to DE +2040 0CAE 7D STORED: LD A,L ; Get LSB of new RAM top +2041 0CAF 93 SUB E ; Subtract LSB of string space +2042 0CB0 5F LD E,A ; Save LSB +2043 0CB1 7C LD A,H ; Get MSB of new RAM top +2044 0CB2 9A SBC A,D ; Subtract MSB of string space +2045 0CB3 57 LD D,A ; Save MSB +2046 0CB4 DA 37 07 JP C,OMERR ; ?OM Error if not enough mem +2047 0CB7 E5 PUSH HL ; Save RAM top +2048 0CB8 2A 86 31 LD HL,(PROGND) ; Get program end +2049 0CBB 01 28 00 LD BC,40 ; 40 Bytes minimum working RAM +2050 0CBE 09 ADD HL,BC ; Get lowest address +2051 0CBF CD 0A 0A CALL CPDEHL ; Enough memory? +2052 0CC2 D2 37 07 JP NC,OMERR ; No - ?OM Error +2053 0CC5 EB EX DE,HL ; RAM top to HL +2054 0CC6 22 0A 31 LD (STRSPC),HL ; Set new string space +2055 0CC9 E1 POP HL ; End of memory to use +2056 0CCA 22 5F 31 LD (LSTRAM),HL ; Set new top of RAM +2057 0CCD E1 POP HL ; Restore code string address +2058 0CCE C3 5E 08 JP INTVAR ; Initialise variables +2059 0CD1 +2060 0CD1 CA 5A 08 RUN: JP Z,RUNFST ; RUN from start if just RUN +2061 0CD4 CD 5E 08 CALL INTVAR ; Initialise variables +2062 0CD7 01 5A 0B LD BC,RUNCNT ; Execution driver loop +2063 0CDA C3 ED 0C JP RUNLIN ; RUN from line number +2064 0CDD +2065 0CDD 0E 03 GOSUB: LD C,3 ; 3 Levels of stack needed +2066 0CDF CD 1F 07 CALL CHKSTK ; Check for 3 levels of stack +2067 0CE2 C1 POP BC ; Get return address +2068 0CE3 E5 PUSH HL ; Save code string for RETURN +2069 0CE4 E5 PUSH HL ; And for GOSUB routine +2070 0CE5 2A 0C 31 LD HL,(LINEAT) ; Get current line +2071 0CE8 E3 EX (SP),HL ; Into stack - Code string out +2072 0CE9 3E 8C LD A,ZGOSUB ; "GOSUB" token +2073 0CEB F5 PUSH AF ; Save token +2074 0CEC 33 INC SP ; Don't save flags +2075 0CED +2076 0CED C5 RUNLIN: PUSH BC ; Save return address +2077 0CEE CD 66 0C GOTO: CALL ATOH ; ASCII number to DE binary +2078 0CF1 CD 33 0D CALL REM ; Get end of line +2079 0CF4 E5 PUSH HL ; Save end of line +2080 0CF5 2A 0C 31 LD HL,(LINEAT) ; Get current line +2081 0CF8 CD 0A 0A CALL CPDEHL ; Line after current? +2082 0CFB E1 POP HL ; Restore end of line +2083 0CFC 23 INC HL ; Start of next line +2084 0CFD DC 31 08 CALL C,SRCHLP ; Line is after current line +2085 0D00 D4 2E 08 CALL NC,SRCHLN ; Line is before current line +2086 0D03 60 LD H,B ; Set up code string address +2087 0D04 69 LD L,C +2088 0D05 2B DEC HL ; Incremented after +2089 0D06 D8 RET C ; Line found +2090 0D07 1E 0E ULERR: LD E,UL ; ?UL Error +2091 0D09 C3 56 07 JP ERROR ; Output error message +2092 0D0C +2093 0D0C C0 RETURN: RET NZ ; Return if not just RETURN +2094 0D0D 16 FF LD D,-1 ; Flag "GOSUB" search +2095 0D0F CD EB 06 CALL BAKSTK ; Look "GOSUB" block +2096 0D12 F9 LD SP,HL ; Kill all FORs in subroutine +2097 0D13 FE 8C CP ZGOSUB ; Test for "GOSUB" token +2098 0D15 1E 04 LD E,RG ; ?RG Error +2099 0D17 C2 56 07 JP NZ,ERROR ; Error if no "GOSUB" found +2100 0D1A E1 POP HL ; Get RETURN line number +2101 0D1B 22 0C 31 LD (LINEAT),HL ; Save as current +2102 0D1E 23 INC HL ; Was it from direct statement? +2103 0D1F 7C LD A,H +2104 0D20 B5 OR L ; Return to line +2105 0D21 C2 2B 0D JP NZ,RETLIN ; No - Return to line +2106 0D24 3A 7C 31 LD A,(LSTBIN) ; Any INPUT in subroutine? +2107 0D27 B7 OR A ; If so buffer is corrupted +2108 0D28 C2 8C 07 JP NZ,POPNOK ; Yes - Go to command mode +2109 0D2B 21 5A 0B RETLIN: LD HL,RUNCNT ; Execution driver loop +2110 0D2E E3 EX (SP),HL ; Into stack - Code string out +2111 0D2F 3E .BYTE 3EH ; Skip "POP HL" +2112 0D30 E1 NXTDTA: POP HL ; Restore code string address +2113 0D31 +2114 0D31 01 3A DATA: .BYTE 01H,3AH ; ':' End of statement +2115 0D33 0E 00 REM: LD C,0 ; 00 End of statement +2116 0D35 06 00 LD B,0 +2117 0D37 79 NXTSTL: LD A,C ; Statement and byte +2118 0D38 48 LD C,B +2119 0D39 47 LD B,A ; Statement end byte +2120 0D3A 7E NXTSTT: LD A,(HL) ; Get byte +2121 0D3B B7 OR A ; End of line? +2122 0D3C C8 RET Z ; Yes - Exit +2123 0D3D B8 CP B ; End of statement? +2124 0D3E C8 RET Z ; Yes - Exit +2125 0D3F 23 INC HL ; Next byte +2126 0D40 FE 22 CP '"' ; Literal string? +2127 0D42 CA 37 0D JP Z,NXTSTL ; Yes - Look for another '"' +2128 0D45 C3 3A 0D JP NXTSTT ; Keep looking +2129 0D48 +2130 0D48 CD FD 11 LET: CALL GETVAR ; Get variable name +2131 0D4B CD 10 0A CALL CHKSYN ; Make sure "=" follows +2132 0D4E B4 .BYTE ZEQUAL ; "=" token +2133 0D4F D5 PUSH DE ; Save address of variable +2134 0D50 3A 5D 31 LD A,(TYPE) ; Get data type +2135 0D53 F5 PUSH AF ; Save type +2136 0D54 CD 19 10 CALL EVAL ; Evaluate expression +2137 0D57 F1 POP AF ; Restore type +2138 0D58 E3 EX (SP),HL ; Save code - Get var addr +2139 0D59 22 7E 31 LD (BRKLIN),HL ; Save address of variable +2140 0D5C 1F RRA ; Adjust type +2141 0D5D CD 0C 10 CALL CHKTYP ; Check types are the same +2142 0D60 CA 9B 0D JP Z,LETNUM ; Numeric - Move value +2143 0D63 E5 LETSTR: PUSH HL ; Save address of string var +2144 0D64 2A 94 31 LD HL,(FPREG) ; Pointer to string entry +2145 0D67 E5 PUSH HL ; Save it on stack +2146 0D68 23 INC HL ; Skip over length +2147 0D69 23 INC HL +2148 0D6A 5E LD E,(HL) ; LSB of string address +2149 0D6B 23 INC HL +2150 0D6C 56 LD D,(HL) ; MSB of string address +2151 0D6D 2A 0E 31 LD HL,(BASTXT) ; Point to start of program +2152 0D70 CD 0A 0A CALL CPDEHL ; Is string before program? +2153 0D73 D2 8A 0D JP NC,CRESTR ; Yes - Create string entry +2154 0D76 2A 0A 31 LD HL,(STRSPC) ; Point to string space +2155 0D79 CD 0A 0A CALL CPDEHL ; Is string literal in program? +2156 0D7C D1 POP DE ; Restore address of string +2157 0D7D D2 92 0D JP NC,MVSTPT ; Yes - Set up pointer +2158 0D80 21 6F 31 LD HL,TMPSTR ; Temporary string pool +2159 0D83 CD 0A 0A CALL CPDEHL ; Is string in temporary pool? +2160 0D86 D2 92 0D JP NC,MVSTPT ; No - Set up pointer +2161 0D89 3E .BYTE 3EH ; Skip "POP DE" +2162 0D8A D1 CRESTR: POP DE ; Restore address of string +2163 0D8B CD 41 16 CALL BAKTMP ; Back to last tmp-str entry +2164 0D8E EB EX DE,HL ; Address of string entry +2165 0D8F CD 7A 14 CALL SAVSTR ; Save string in string area +2166 0D92 CD 41 16 MVSTPT: CALL BAKTMP ; Back to last tmp-str entry +2167 0D95 E1 POP HL ; Get string pointer +2168 0D96 CD 44 1A CALL DETHL4 ; Move string pointer to var +2169 0D99 E1 POP HL ; Restore code string address +2170 0D9A C9 RET +2171 0D9B +2172 0D9B E5 LETNUM: PUSH HL ; Save address of variable +2173 0D9C CD 41 1A CALL FPTHL ; Move value to variable +2174 0D9F D1 POP DE ; Restore address of variable +2175 0DA0 E1 POP HL ; Restore code string address +2176 0DA1 C9 RET +2177 0DA2 +2178 0DA2 CD 68 17 ON: CALL GETINT ; Get integer 0-255 +2179 0DA5 7E LD A,(HL) ; Get "GOTO" or "GOSUB" token +2180 0DA6 47 LD B,A ; Save in B +2181 0DA7 FE 8C CP ZGOSUB ; "GOSUB" token? +2182 0DA9 CA B1 0D JP Z,ONGO ; Yes - Find line number +2183 0DAC CD 10 0A CALL CHKSYN ; Make sure it's "GOTO" +2184 0DAF 88 .BYTE ZGOTO ; "GOTO" token +2185 0DB0 2B DEC HL ; Cancel increment +2186 0DB1 4B ONGO: LD C,E ; Integer of branch value +2187 0DB2 0D ONGOLP: DEC C ; Count branches +2188 0DB3 78 LD A,B ; Get "GOTO" or "GOSUB" token +2189 0DB4 CA 82 0B JP Z,ONJMP ; Go to that line if right one +2190 0DB7 CD 67 0C CALL GETLN ; Get line number to DE +2191 0DBA FE 2C CP ',' ; Another line number? +2192 0DBC C0 RET NZ ; No - Drop through +2193 0DBD C3 B2 0D JP ONGOLP ; Yes - loop +2194 0DC0 +2195 0DC0 CD 19 10 IF: CALL EVAL ; Evaluate expression +2196 0DC3 7E LD A,(HL) ; Get token +2197 0DC4 FE 88 CP ZGOTO ; "GOTO" token? +2198 0DC6 CA CE 0D JP Z,IFGO ; Yes - Get line +2199 0DC9 CD 10 0A CALL CHKSYN ; Make sure it's "THEN" +2200 0DCC A9 .BYTE ZTHEN ; "THEN" token +2201 0DCD 2B DEC HL ; Cancel increment +2202 0DCE CD 0A 10 IFGO: CALL TSTNUM ; Make sure it's numeric +2203 0DD1 CD E9 19 CALL TSTSGN ; Test state of expression +2204 0DD4 CA 33 0D JP Z,REM ; False - Drop through +2205 0DD7 CD 9A 0B CALL GETCHR ; Get next character +2206 0DDA DA EE 0C JP C,GOTO ; Number - GOTO that line +2207 0DDD C3 81 0B JP IFJMP ; Otherwise do statement +2208 0DE0 +2209 0DE0 2B MRPRNT: DEC HL ; DEC 'cos GETCHR INCs +2210 0DE1 CD 9A 0B CALL GETCHR ; Get next character +2211 0DE4 CA 42 0E PRINT: JP Z,PRNTCRLF ; CRLF if just PRINT +2212 0DE7 C8 PRNTLP: RET Z ; End of list - Exit +2213 0DE8 FE A5 CP ZTAB ; "TAB(" token? +2214 0DEA CA 75 0E JP Z,DOTAB ; Yes - Do TAB routine +2215 0DED FE A8 CP ZSPC ; "SPC(" token? +2216 0DEF CA 75 0E JP Z,DOTAB ; Yes - Do SPC routine +2217 0DF2 E5 PUSH HL ; Save code string address +2218 0DF3 FE 2C CP ',' ; Comma? +2219 0DF5 CA 5E 0E JP Z,DOCOM ; Yes - Move to next zone +2220 0DF8 FE 3B CP 59 ;";" ; Semi-colon? +2221 0DFA CA 98 0E JP Z,NEXITM ; Do semi-colon routine +2222 0DFD C1 POP BC ; Code string address to BC +2223 0DFE CD 19 10 CALL EVAL ; Evaluate expression +2224 0E01 E5 PUSH HL ; Save code string address +2225 0E02 3A 5D 31 LD A,(TYPE) ; Get variable type +2226 0E05 B7 OR A ; Is it a string variable? +2227 0E06 C2 2E 0E JP NZ,PRNTST ; Yes - Output string contents +2228 0E09 CD 8E 1B CALL NUMASC ; Convert number to text +2229 0E0C CD 9E 14 CALL CRTST ; Create temporary string +2230 0E0F 36 20 LD (HL),' ' ; Followed by a space +2231 0E11 2A 94 31 LD HL,(FPREG) ; Get length of output +2232 0E14 34 INC (HL) ; Plus 1 for the space +2233 0E15 2A 94 31 LD HL,(FPREG) ; < Not needed > +2234 0E18 3A F2 30 LD A,(LWIDTH) ; Get width of line +2235 0E1B 47 LD B,A ; To B +2236 0E1C 04 INC B ; Width 255 (No limit)? +2237 0E1D CA 2A 0E JP Z,PRNTNB ; Yes - Output number string +2238 0E20 04 INC B ; Adjust it +2239 0E21 3A 5B 31 LD A,(CURPOS) ; Get cursor position +2240 0E24 86 ADD A,(HL) ; Add length of string +2241 0E25 3D DEC A ; Adjust it +2242 0E26 B8 CP B ; Will output fit on this line? +2243 0E27 D4 42 0E CALL NC,PRNTCRLF ; No - CRLF first +2244 0E2A CD E3 14 PRNTNB: CALL PRS1 ; Output string at (HL) +2245 0E2D AF XOR A ; Skip CALL by setting 'z' flag +2246 0E2E C4 E3 14 PRNTST: CALL NZ,PRS1 ; Output string at (HL) +2247 0E31 E1 POP HL ; Restore code string address +2248 0E32 C3 E0 0D JP MRPRNT ; See if more to PRINT +2249 0E35 +2250 0E35 3A 5B 31 STTLIN: LD A,(CURPOS) ; Make sure on new line +2251 0E38 B7 OR A ; Already at start? +2252 0E39 C8 RET Z ; Yes - Do nothing +2253 0E3A C3 42 0E JP PRNTCRLF ; Start a new line +2254 0E3D +2255 0E3D 36 00 ENDINP: LD (HL),0 ; Mark end of buffer +2256 0E3F 21 10 31 LD HL,BUFFER-1 ; Point to buffer +2257 0E42 3E 0D PRNTCRLF: LD A,CR ; Load a CR +2258 0E44 CD 1B 0A CALL OUTC ; Output character +2259 0E47 3E 0A LD A,LF ; Load a LF +2260 0E49 CD 1B 0A CALL OUTC ; Output character +2261 0E4C AF DONULL: XOR A ; Set to position 0 +2262 0E4D 32 5B 31 LD (CURPOS),A ; Store it +2263 0E50 3A F1 30 LD A,(NULLS) ; Get number of nulls +2264 0E53 3D NULLP: DEC A ; Count them +2265 0E54 C8 RET Z ; Return if done +2266 0E55 F5 PUSH AF ; Save count +2267 0E56 AF XOR A ; Load a null +2268 0E57 CD 1B 0A CALL OUTC ; Output it +2269 0E5A F1 POP AF ; Restore count +2270 0E5B C3 53 0E JP NULLP ; Keep counting +2271 0E5E +2272 0E5E 3A F3 30 DOCOM: LD A,(COMMAN) ; Get comma width +2273 0E61 47 LD B,A ; Save in B +2274 0E62 3A 5B 31 LD A,(CURPOS) ; Get current position +2275 0E65 B8 CP B ; Within the limit? +2276 0E66 D4 42 0E CALL NC,PRNTCRLF ; No - output CRLF +2277 0E69 D2 98 0E JP NC,NEXITM ; Get next item +2278 0E6C D6 0E ZONELP: SUB 14 ; Next zone of 14 characters +2279 0E6E D2 6C 0E JP NC,ZONELP ; Repeat if more zones +2280 0E71 2F CPL ; Number of spaces to output +2281 0E72 C3 8D 0E JP ASPCS ; Output them +2282 0E75 +2283 0E75 F5 DOTAB: PUSH AF ; Save token +2284 0E76 CD 65 17 CALL FNDNUM ; Evaluate expression +2285 0E79 CD 10 0A CALL CHKSYN ; Make sure ")" follows +2286 0E7C 29 .BYTE ")" +2287 0E7D 2B DEC HL ; Back space on to ")" +2288 0E7E F1 POP AF ; Restore token +2289 0E7F D6 A8 SUB ZSPC ; Was it "SPC(" ? +2290 0E81 E5 PUSH HL ; Save code string address +2291 0E82 CA 88 0E JP Z,DOSPC ; Yes - Do 'E' spaces +2292 0E85 3A 5B 31 LD A,(CURPOS) ; Get current position +2293 0E88 2F DOSPC: CPL ; Number of spaces to print to +2294 0E89 83 ADD A,E ; Total number to print +2295 0E8A D2 98 0E JP NC,NEXITM ; TAB < Current POS(X) +2296 0E8D 3C ASPCS: INC A ; Output A spaces +2297 0E8E 47 LD B,A ; Save number to print +2298 0E8F 3E 20 LD A,' ' ; Space +2299 0E91 CD 1B 0A SPCLP: CALL OUTC ; Output character in A +2300 0E94 05 DEC B ; Count them +2301 0E95 C2 91 0E JP NZ,SPCLP ; Repeat if more +2302 0E98 E1 NEXITM: POP HL ; Restore code string address +2303 0E99 CD 9A 0B CALL GETCHR ; Get next character +2304 0E9C C3 E7 0D JP PRNTLP ; More to print +2305 0E9F +2306 0E9F 3F5265646F20REDO: .BYTE "?Redo from start",CR,LF,0 +2306 0EA5 66726F6D2073746172740D0A00 +2307 0EB2 +2308 0EB2 3A 7D 31 BADINP: LD A,(READFG) ; READ or INPUT? +2309 0EB5 B7 OR A +2310 0EB6 C2 3C 07 JP NZ,DATSNR ; READ - ?SN Error +2311 0EB9 C1 POP BC ; Throw away code string addr +2312 0EBA 21 9F 0E LD HL,REDO ; "Redo from start" message +2313 0EBD CD E0 14 CALL PRS ; Output string +2314 0EC0 C3 8D 08 JP DOAGN ; Do last INPUT again +2315 0EC3 +2316 0EC3 CD 4B 14 INPUT: CALL IDTEST ; Test for illegal direct +2317 0EC6 7E LD A,(HL) ; Get character after "INPUT" +2318 0EC7 FE 22 CP '"' ; Is there a prompt string? +2319 0EC9 3E 00 LD A,0 ; Clear A and leave flags +2320 0ECB 32 F5 30 LD (CTLOFG),A ; Enable output +2321 0ECE C2 DD 0E JP NZ,NOPMPT ; No prompt - get input +2322 0ED1 CD 9F 14 CALL QTSTR ; Get string terminated by '"' +2323 0ED4 CD 10 0A CALL CHKSYN ; Check for ';' after prompt +2324 0ED7 3B .BYTE ';' +2325 0ED8 E5 PUSH HL ; Save code string address +2326 0ED9 CD E3 14 CALL PRS1 ; Output prompt string +2327 0EDC 3E .BYTE 3EH ; Skip "PUSH HL" +2328 0EDD E5 NOPMPT: PUSH HL ; Save code string address +2329 0EDE CD 91 08 CALL PROMPT ; Get input with "? " prompt +2330 0EE1 C1 POP BC ; Restore code string address +2331 0EE2 DA E9 0B JP C,INPBRK ; Break pressed - Exit +2332 0EE5 23 INC HL ; Next byte +2333 0EE6 7E LD A,(HL) ; Get it +2334 0EE7 B7 OR A ; End of line? +2335 0EE8 2B DEC HL ; Back again +2336 0EE9 C5 PUSH BC ; Re-save code string address +2337 0EEA CA 30 0D JP Z,NXTDTA ; Yes - Find next DATA stmt +2338 0EED 36 2C LD (HL),',' ; Store comma as separator +2339 0EEF C3 F7 0E JP NXTITM ; Get next item +2340 0EF2 +2341 0EF2 E5 READ: PUSH HL ; Save code string address +2342 0EF3 2A 8C 31 LD HL,(NXTDAT) ; Next DATA statement +2343 0EF6 F6 .BYTE 0F6H ; Flag "READ" +2344 0EF7 AF NXTITM: XOR A ; Flag "INPUT" +2345 0EF8 32 7D 31 LD (READFG),A ; Save "READ"/"INPUT" flag +2346 0EFB E3 EX (SP),HL ; Get code str' , Save pointer +2347 0EFC C3 03 0F JP GTVLUS ; Get values +2348 0EFF +2349 0EFF CD 10 0A NEDMOR: CALL CHKSYN ; Check for comma between items +2350 0F02 2C .BYTE ',' +2351 0F03 CD FD 11 GTVLUS: CALL GETVAR ; Get variable name +2352 0F06 E3 EX (SP),HL ; Save code str" , Get pointer +2353 0F07 D5 PUSH DE ; Save variable address +2354 0F08 7E LD A,(HL) ; Get next "INPUT"/"DATA" byte +2355 0F09 FE 2C CP ',' ; Comma? +2356 0F0B CA 2B 0F JP Z,ANTVLU ; Yes - Get another value +2357 0F0E 3A 7D 31 LD A,(READFG) ; Is it READ? +2358 0F11 B7 OR A +2359 0F12 C2 98 0F JP NZ,FDTLP ; Yes - Find next DATA stmt +2360 0F15 3E 3F LD A,'?' ; More INPUT needed +2361 0F17 CD 1B 0A CALL OUTC ; Output character +2362 0F1A CD 91 08 CALL PROMPT ; Get INPUT with prompt +2363 0F1D D1 POP DE ; Variable address +2364 0F1E C1 POP BC ; Code string address +2365 0F1F DA E9 0B JP C,INPBRK ; Break pressed +2366 0F22 23 INC HL ; Point to next DATA byte +2367 0F23 7E LD A,(HL) ; Get byte +2368 0F24 B7 OR A ; Is it zero (No input) ? +2369 0F25 2B DEC HL ; Back space INPUT pointer +2370 0F26 C5 PUSH BC ; Save code string address +2371 0F27 CA 30 0D JP Z,NXTDTA ; Find end of buffer +2372 0F2A D5 PUSH DE ; Save variable address +2373 0F2B 3A 5D 31 ANTVLU: LD A,(TYPE) ; Check data type +2374 0F2E B7 OR A ; Is it numeric? +2375 0F2F CA 55 0F JP Z,INPBIN ; Yes - Convert to binary +2376 0F32 CD 9A 0B CALL GETCHR ; Get next character +2377 0F35 57 LD D,A ; Save input character +2378 0F36 47 LD B,A ; Again +2379 0F37 FE 22 CP '"' ; Start of literal sting? +2380 0F39 CA 49 0F JP Z,STRENT ; Yes - Create string entry +2381 0F3C 3A 7D 31 LD A,(READFG) ; "READ" or "INPUT" ? +2382 0F3F B7 OR A +2383 0F40 57 LD D,A ; Save 00 if "INPUT" +2384 0F41 CA 46 0F JP Z,ITMSEP ; "INPUT" - End with 00 +2385 0F44 16 3A LD D,':' ; "DATA" - End with 00 or ':' +2386 0F46 06 2C ITMSEP: LD B,',' ; Item separator +2387 0F48 2B DEC HL ; Back space for DTSTR +2388 0F49 CD A2 14 STRENT: CALL DTSTR ; Get string terminated by D +2389 0F4C EB EX DE,HL ; String address to DE +2390 0F4D 21 60 0F LD HL,LTSTND ; Where to go after LETSTR +2391 0F50 E3 EX (SP),HL ; Save HL , get input pointer +2392 0F51 D5 PUSH DE ; Save address of string +2393 0F52 C3 63 0D JP LETSTR ; Assign string to variable +2394 0F55 +2395 0F55 CD 9A 0B INPBIN: CALL GETCHR ; Get next character +2396 0F58 CD F0 1A CALL ASCTFP ; Convert ASCII to FP number +2397 0F5B E3 EX (SP),HL ; Save input ptr, Get var addr +2398 0F5C CD 41 1A CALL FPTHL ; Move FPREG to variable +2399 0F5F E1 POP HL ; Restore input pointer +2400 0F60 2B LTSTND: DEC HL ; DEC 'cos GETCHR INCs +2401 0F61 CD 9A 0B CALL GETCHR ; Get next character +2402 0F64 CA 6C 0F JP Z,MORDT ; End of line - More needed? +2403 0F67 FE 2C CP ',' ; Another value? +2404 0F69 C2 B2 0E JP NZ,BADINP ; No - Bad input +2405 0F6C E3 MORDT: EX (SP),HL ; Get code string address +2406 0F6D 2B DEC HL ; DEC 'cos GETCHR INCs +2407 0F6E CD 9A 0B CALL GETCHR ; Get next character +2408 0F71 C2 FF 0E JP NZ,NEDMOR ; More needed - Get it +2409 0F74 D1 POP DE ; Restore DATA pointer +2410 0F75 3A 7D 31 LD A,(READFG) ; "READ" or "INPUT" ? +2411 0F78 B7 OR A +2412 0F79 EB EX DE,HL ; DATA pointer to HL +2413 0F7A C2 C0 0B JP NZ,UPDATA ; Update DATA pointer if "READ" +2414 0F7D D5 PUSH DE ; Save code string address +2415 0F7E B6 OR (HL) ; More input given? +2416 0F7F 21 87 0F LD HL,EXTIG ; "?Extra ignored" message +2417 0F82 C4 E0 14 CALL NZ,PRS ; Output string if extra given +2418 0F85 E1 POP HL ; Restore code string address +2419 0F86 C9 RET +2420 0F87 +2421 0F87 3F4578747261EXTIG: .BYTE "?Extra ignored",CR,LF,0 +2421 0F8D 2069676E6F7265640D0A00 +2422 0F98 +2423 0F98 CD 31 0D FDTLP: CALL DATA ; Get next statement +2424 0F9B B7 OR A ; End of line? +2425 0F9C C2 B1 0F JP NZ,FANDT ; No - See if DATA statement +2426 0F9F 23 INC HL +2427 0FA0 7E LD A,(HL) ; End of program? +2428 0FA1 23 INC HL +2429 0FA2 B6 OR (HL) ; 00 00 Ends program +2430 0FA3 1E 06 LD E,OD ; ?OD Error +2431 0FA5 CA 56 07 JP Z,ERROR ; Yes - Out of DATA +2432 0FA8 23 INC HL +2433 0FA9 5E LD E,(HL) ; LSB of line number +2434 0FAA 23 INC HL +2435 0FAB 56 LD D,(HL) ; MSB of line number +2436 0FAC EB EX DE,HL +2437 0FAD 22 79 31 LD (DATLIN),HL ; Set line of current DATA item +2438 0FB0 EB EX DE,HL +2439 0FB1 CD 9A 0B FANDT: CALL GETCHR ; Get next character +2440 0FB4 FE 83 CP ZDATA ; "DATA" token +2441 0FB6 C2 98 0F JP NZ,FDTLP ; No "DATA" - Keep looking +2442 0FB9 C3 2B 0F JP ANTVLU ; Found - Convert input +2443 0FBC +2444 0FBC 11 00 00 NEXT: LD DE,0 ; In case no index given +2445 0FBF C4 FD 11 NEXT1: CALL NZ,GETVAR ; Get index address +2446 0FC2 22 7E 31 LD (BRKLIN),HL ; Save code string address +2447 0FC5 CD EB 06 CALL BAKSTK ; Look for "FOR" block +2448 0FC8 C2 48 07 JP NZ,NFERR ; No "FOR" - ?NF Error +2449 0FCB F9 LD SP,HL ; Clear nested loops +2450 0FCC D5 PUSH DE ; Save index address +2451 0FCD 7E LD A,(HL) ; Get sign of STEP +2452 0FCE 23 INC HL +2453 0FCF F5 PUSH AF ; Save sign of STEP +2454 0FD0 D5 PUSH DE ; Save index address +2455 0FD1 CD 27 1A CALL PHLTFP ; Move index value to FPREG +2456 0FD4 E3 EX (SP),HL ; Save address of TO value +2457 0FD5 E5 PUSH HL ; Save address of index +2458 0FD6 CD 94 17 CALL ADDPHL ; Add STEP to index value +2459 0FD9 E1 POP HL ; Restore address of index +2460 0FDA CD 41 1A CALL FPTHL ; Move value to index variable +2461 0FDD E1 POP HL ; Restore address of TO value +2462 0FDE CD 38 1A CALL LOADFP ; Move TO value to BCDE +2463 0FE1 E5 PUSH HL ; Save address of line of FOR +2464 0FE2 CD 64 1A CALL CMPNUM ; Compare index with TO value +2465 0FE5 E1 POP HL ; Restore address of line num +2466 0FE6 C1 POP BC ; Address of sign of STEP +2467 0FE7 90 SUB B ; Compare with expected sign +2468 0FE8 CD 38 1A CALL LOADFP ; BC = Loop stmt,DE = Line num +2469 0FEB CA F7 0F JP Z,KILFOR ; Loop finished - Terminate it +2470 0FEE EB EX DE,HL ; Loop statement line number +2471 0FEF 22 0C 31 LD (LINEAT),HL ; Set loop line number +2472 0FF2 69 LD L,C ; Set code string to loop +2473 0FF3 60 LD H,B +2474 0FF4 C3 56 0B JP PUTFID ; Put back "FOR" and continue +2475 0FF7 +2476 0FF7 F9 KILFOR: LD SP,HL ; Remove "FOR" block +2477 0FF8 2A 7E 31 LD HL,(BRKLIN) ; Code string after "NEXT" +2478 0FFB 7E LD A,(HL) ; Get next byte in code string +2479 0FFC FE 2C CP ',' ; More NEXTs ? +2480 0FFE C2 5A 0B JP NZ,RUNCNT ; No - Do next statement +2481 1001 CD 9A 0B CALL GETCHR ; Position to index name +2482 1004 CD BF 0F CALL NEXT1 ; Re-enter NEXT routine +2483 1007 ; < will not RETurn to here , Exit to RUNCNT or Loop > +2484 1007 +2485 1007 CD 19 10 GETNUM: CALL EVAL ; Get a numeric expression +2486 100A F6 TSTNUM: .BYTE 0F6H ; Clear carry (numeric) +2487 100B 37 TSTSTR: SCF ; Set carry (string) +2488 100C 3A 5D 31 CHKTYP: LD A,(TYPE) ; Check types match +2489 100F 8F ADC A,A ; Expected + actual +2490 1010 B7 OR A ; Clear carry , set parity +2491 1011 E8 RET PE ; Even parity - Types match +2492 1012 C3 54 07 JP TMERR ; Different types - Error +2493 1015 +2494 1015 CD 10 0A OPNPAR: CALL CHKSYN ; Make sure "(" follows +2495 1018 28 .BYTE "(" +2496 1019 2B EVAL: DEC HL ; Evaluate expression & save +2497 101A 16 00 LD D,0 ; Precedence value +2498 101C D5 EVAL1: PUSH DE ; Save precedence +2499 101D 0E 01 LD C,1 +2500 101F CD 1F 07 CALL CHKSTK ; Check for 1 level of stack +2501 1022 CD 90 10 CALL OPRND ; Get next expression value +2502 1025 22 80 31 EVAL2: LD (NXTOPR),HL ; Save address of next operator +2503 1028 2A 80 31 EVAL3: LD HL,(NXTOPR) ; Restore address of next opr +2504 102B C1 POP BC ; Precedence value and operator +2505 102C 78 LD A,B ; Get precedence value +2506 102D FE 78 CP 78H ; "AND" or "OR" ? +2507 102F D4 0A 10 CALL NC,TSTNUM ; No - Make sure it's a number +2508 1032 7E LD A,(HL) ; Get next operator / function +2509 1033 16 00 LD D,0 ; Clear Last relation +2510 1035 D6 B3 RLTLP: SUB ZGTR ; ">" Token +2511 1037 DA 51 10 JP C,FOPRND ; + - * / ^ AND OR - Test it +2512 103A FE 03 CP ZLTH+1-ZGTR ; < = > +2513 103C D2 51 10 JP NC,FOPRND ; Function - Call it +2514 103F FE 01 CP ZEQUAL-ZGTR ; "=" +2515 1041 17 RLA ; <- Test for legal +2516 1042 AA XOR D ; <- combinations of < = > +2517 1043 BA CP D ; <- by combining last token +2518 1044 57 LD D,A ; <- with current one +2519 1045 DA 42 07 JP C,SNERR ; Error if "<<' '==" or ">>" +2520 1048 22 75 31 LD (CUROPR),HL ; Save address of current token +2521 104B CD 9A 0B CALL GETCHR ; Get next character +2522 104E C3 35 10 JP RLTLP ; Treat the two as one +2523 1051 +2524 1051 7A FOPRND: LD A,D ; < = > found ? +2525 1052 B7 OR A +2526 1053 C2 78 11 JP NZ,TSTRED ; Yes - Test for reduction +2527 1056 7E LD A,(HL) ; Get operator token +2528 1057 22 75 31 LD (CUROPR),HL ; Save operator address +2529 105A D6 AC SUB ZPLUS ; Operator or function? +2530 105C D8 RET C ; Neither - Exit +2531 105D FE 07 CP ZOR+1-ZPLUS ; Is it + - * / ^ AND OR ? +2532 105F D0 RET NC ; No - Exit +2533 1060 5F LD E,A ; Coded operator +2534 1061 3A 5D 31 LD A,(TYPE) ; Get data type +2535 1064 3D DEC A ; FF = numeric , 00 = string +2536 1065 B3 OR E ; Combine with coded operator +2537 1066 7B LD A,E ; Get coded operator +2538 1067 CA D6 15 JP Z,CONCAT ; String concatenation +2539 106A 07 RLCA ; Times 2 +2540 106B 83 ADD A,E ; Times 3 +2541 106C 5F LD E,A ; To DE (D is 0) +2542 106D 21 34 06 LD HL,PRITAB ; Precedence table +2543 1070 19 ADD HL,DE ; To the operator concerned +2544 1071 78 LD A,B ; Last operator precedence +2545 1072 56 LD D,(HL) ; Get evaluation precedence +2546 1073 BA CP D ; Compare with eval precedence +2547 1074 D0 RET NC ; Exit if higher precedence +2548 1075 23 INC HL ; Point to routine address +2549 1076 CD 0A 10 CALL TSTNUM ; Make sure it's a number +2550 1079 +2551 1079 C5 STKTHS: PUSH BC ; Save last precedence & token +2552 107A 01 28 10 LD BC,EVAL3 ; Where to go on prec' break +2553 107D C5 PUSH BC ; Save on stack for return +2554 107E 43 LD B,E ; Save operator +2555 107F 4A LD C,D ; Save precedence +2556 1080 CD 1A 1A CALL STAKFP ; Move value to stack +2557 1083 58 LD E,B ; Restore operator +2558 1084 51 LD D,C ; Restore precedence +2559 1085 4E LD C,(HL) ; Get LSB of routine address +2560 1086 23 INC HL +2561 1087 46 LD B,(HL) ; Get MSB of routine address +2562 1088 23 INC HL +2563 1089 C5 PUSH BC ; Save routine address +2564 108A 2A 75 31 LD HL,(CUROPR) ; Address of current operator +2565 108D C3 1C 10 JP EVAL1 ; Loop until prec' break +2566 1090 +2567 1090 AF OPRND: XOR A ; Get operand routine +2568 1091 32 5D 31 LD (TYPE),A ; Set numeric expected +2569 1094 CD 9A 0B CALL GETCHR ; Get next character +2570 1097 1E 24 LD E,MO ; ?MO Error +2571 1099 CA 56 07 JP Z,ERROR ; No operand - Error +2572 109C DA F0 1A JP C,ASCTFP ; Number - Get value +2573 109F CD 38 0C CALL CHKLTR ; See if a letter +2574 10A2 D2 F7 10 JP NC,CONVAR ; Letter - Find variable +2575 10A5 FE 26 CP '&' ; &H = HEX, &B = BINARY +2576 10A7 20 12 JR NZ, NOTAMP +2577 10A9 CD 9A 0B CALL GETCHR ; Get next character +2578 10AC FE 48 CP 'H' ; Hex number indicated? [function added] +2579 10AE CA 34 1F JP Z,HEXTFP ; Convert Hex to FPREG +2580 10B1 FE 42 CP 'B' ; Binary number indicated? [function added] +2581 10B3 CA A4 1F JP Z,BINTFP ; Convert Bin to FPREG +2582 10B6 1E 02 LD E,SN ; If neither then a ?SN Error +2583 10B8 CA 56 07 JP Z,ERROR ; +2584 10BB FE AC NOTAMP: CP ZPLUS ; '+' Token ? +2585 10BD CA 90 10 JP Z,OPRND ; Yes - Look for operand +2586 10C0 FE 2E CP '.' ; '.' ? +2587 10C2 CA F0 1A JP Z,ASCTFP ; Yes - Create FP number +2588 10C5 FE AD CP ZMINUS ; '-' Token ? +2589 10C7 CA E6 10 JP Z,MINUS ; Yes - Do minus +2590 10CA FE 22 CP '"' ; Literal string ? +2591 10CC CA 9F 14 JP Z,QTSTR ; Get string terminated by '"' +2592 10CF FE AA CP ZNOT ; "NOT" Token ? +2593 10D1 CA D8 11 JP Z,EVNOT ; Yes - Eval NOT expression +2594 10D4 FE A7 CP ZFN ; "FN" Token ? +2595 10D6 CA 03 14 JP Z,DOFN ; Yes - Do FN routine +2596 10D9 D6 B6 SUB ZSGN ; Is it a function? +2597 10DB D2 08 11 JP NC,FNOFST ; Yes - Evaluate function +2598 10DE CD 15 10 EVLPAR: CALL OPNPAR ; Evaluate expression in "()" +2599 10E1 CD 10 0A CALL CHKSYN ; Make sure ")" follows +2600 10E4 29 .BYTE ")" +2601 10E5 C9 RET +2602 10E6 +2603 10E6 16 7D MINUS: LD D,7DH ; '-' precedence +2604 10E8 CD 1C 10 CALL EVAL1 ; Evaluate until prec' break +2605 10EB 2A 80 31 LD HL,(NXTOPR) ; Get next operator address +2606 10EE E5 PUSH HL ; Save next operator address +2607 10EF CD 12 1A CALL INVSGN ; Negate value +2608 10F2 CD 0A 10 RETNUM: CALL TSTNUM ; Make sure it's a number +2609 10F5 E1 POP HL ; Restore next operator address +2610 10F6 C9 RET +2611 10F7 +2612 10F7 CD FD 11 CONVAR: CALL GETVAR ; Get variable address to DE +2613 10FA E5 FRMEVL: PUSH HL ; Save code string address +2614 10FB EB EX DE,HL ; Variable address to HL +2615 10FC 22 94 31 LD (FPREG),HL ; Save address of variable +2616 10FF 3A 5D 31 LD A,(TYPE) ; Get type +2617 1102 B7 OR A ; Numeric? +2618 1103 CC 27 1A CALL Z,PHLTFP ; Yes - Move contents to FPREG +2619 1106 E1 POP HL ; Restore code string address +2620 1107 C9 RET +2621 1108 +2622 1108 06 00 FNOFST: LD B,0 ; Get address of function +2623 110A 07 RLCA ; Double function offset +2624 110B 4F LD C,A ; BC = Offset in function table +2625 110C C5 PUSH BC ; Save adjusted token value +2626 110D CD 9A 0B CALL GETCHR ; Get next character +2627 1110 79 LD A,C ; Get adjusted token value +2628 1111 FE 31 CP 2*(ZLEFT-ZSGN)-1; Adj' LEFT$,RIGHT$ or MID$ ? +2629 1113 DA 2F 11 JP C,FNVAL ; No - Do function +2630 1116 CD 15 10 CALL OPNPAR ; Evaluate expression (X,... +2631 1119 CD 10 0A CALL CHKSYN ; Make sure ',' follows +2632 111C 2C .BYTE ',' +2633 111D CD 0B 10 CALL TSTSTR ; Make sure it's a string +2634 1120 EB EX DE,HL ; Save code string address +2635 1121 2A 94 31 LD HL,(FPREG) ; Get address of string +2636 1124 E3 EX (SP),HL ; Save address of string +2637 1125 E5 PUSH HL ; Save adjusted token value +2638 1126 EB EX DE,HL ; Restore code string address +2639 1127 CD 68 17 CALL GETINT ; Get integer 0-255 +2640 112A EB EX DE,HL ; Save code string address +2641 112B E3 EX (SP),HL ; Save integer,HL = adj' token +2642 112C C3 37 11 JP GOFUNC ; Jump to string function +2643 112F +2644 112F CD DE 10 FNVAL: CALL EVLPAR ; Evaluate expression +2645 1132 E3 EX (SP),HL ; HL = Adjusted token value +2646 1133 11 F2 10 LD DE,RETNUM ; Return number from function +2647 1136 D5 PUSH DE ; Save on stack +2648 1137 01 93 04 GOFUNC: LD BC,FNCTAB ; Function routine addresses +2649 113A 09 ADD HL,BC ; Point to right address +2650 113B 4E LD C,(HL) ; Get LSB of address +2651 113C 23 INC HL ; +2652 113D 66 LD H,(HL) ; Get MSB of address +2653 113E 69 LD L,C ; Address to HL +2654 113F E9 JP (HL) ; Jump to function +2655 1140 +2656 1140 15 SGNEXP: DEC D ; Dee to flag negative exponent +2657 1141 FE AD CP ZMINUS ; '-' token ? +2658 1143 C8 RET Z ; Yes - Return +2659 1144 FE 2D CP '-' ; '-' ASCII ? +2660 1146 C8 RET Z ; Yes - Return +2661 1147 14 INC D ; Inc to flag positive exponent +2662 1148 FE 2B CP '+' ; '+' ASCII ? +2663 114A C8 RET Z ; Yes - Return +2664 114B FE AC CP ZPLUS ; '+' token ? +2665 114D C8 RET Z ; Yes - Return +2666 114E 2B DEC HL ; DEC 'cos GETCHR INCs +2667 114F C9 RET ; Return "NZ" +2668 1150 +2669 1150 F6 POR: .BYTE 0F6H ; Flag "OR" +2670 1151 AF PAND: XOR A ; Flag "AND" +2671 1152 F5 PUSH AF ; Save "AND" / "OR" flag +2672 1153 CD 0A 10 CALL TSTNUM ; Make sure it's a number +2673 1156 CD 4C 0C CALL DEINT ; Get integer -32768 to 32767 +2674 1159 F1 POP AF ; Restore "AND" / "OR" flag +2675 115A EB EX DE,HL ; <- Get last +2676 115B C1 POP BC ; <- value +2677 115C E3 EX (SP),HL ; <- from +2678 115D EB EX DE,HL ; <- stack +2679 115E CD 2A 1A CALL FPBCDE ; Move last value to FPREG +2680 1161 F5 PUSH AF ; Save "AND" / "OR" flag +2681 1162 CD 4C 0C CALL DEINT ; Get integer -32768 to 32767 +2682 1165 F1 POP AF ; Restore "AND" / "OR" flag +2683 1166 C1 POP BC ; Get value +2684 1167 79 LD A,C ; Get LSB +2685 1168 21 C1 13 LD HL,ACPASS ; Address of save AC as current +2686 116B C2 73 11 JP NZ,POR1 ; Jump if OR +2687 116E A3 AND E ; "AND" LSBs +2688 116F 4F LD C,A ; Save LSB +2689 1170 78 LD A,B ; Get MBS +2690 1171 A2 AND D ; "AND" MSBs +2691 1172 E9 JP (HL) ; Save AC as current (ACPASS) +2692 1173 +2693 1173 B3 POR1: OR E ; "OR" LSBs +2694 1174 4F LD C,A ; Save LSB +2695 1175 78 LD A,B ; Get MSB +2696 1176 B2 OR D ; "OR" MSBs +2697 1177 E9 JP (HL) ; Save AC as current (ACPASS) +2698 1178 +2699 1178 21 8A 11 TSTRED: LD HL,CMPLOG ; Logical compare routine +2700 117B 3A 5D 31 LD A,(TYPE) ; Get data type +2701 117E 1F RRA ; Carry set = string +2702 117F 7A LD A,D ; Get last precedence value +2703 1180 17 RLA ; Times 2 plus carry +2704 1181 5F LD E,A ; To E +2705 1182 16 64 LD D,64H ; Relational precedence +2706 1184 78 LD A,B ; Get current precedence +2707 1185 BA CP D ; Compare with last +2708 1186 D0 RET NC ; Eval if last was rel' or log' +2709 1187 C3 79 10 JP STKTHS ; Stack this one and get next +2710 118A +2711 118A 8C 11 CMPLOG: .WORD CMPLG1 ; Compare two values / strings +2712 118C 79 CMPLG1: LD A,C ; Get data type +2713 118D B7 OR A +2714 118E 1F RRA +2715 118F C1 POP BC ; Get last expression to BCDE +2716 1190 D1 POP DE +2717 1191 F5 PUSH AF ; Save status +2718 1192 CD 0C 10 CALL CHKTYP ; Check that types match +2719 1195 21 CE 11 LD HL,CMPRES ; Result to comparison +2720 1198 E5 PUSH HL ; Save for RETurn +2721 1199 CA 64 1A JP Z,CMPNUM ; Compare values if numeric +2722 119C AF XOR A ; Compare two strings +2723 119D 32 5D 31 LD (TYPE),A ; Set type to numeric +2724 11A0 D5 PUSH DE ; Save string name +2725 11A1 CD 23 16 CALL GSTRCU ; Get current string +2726 11A4 7E LD A,(HL) ; Get length of string +2727 11A5 23 INC HL +2728 11A6 23 INC HL +2729 11A7 4E LD C,(HL) ; Get LSB of address +2730 11A8 23 INC HL +2731 11A9 46 LD B,(HL) ; Get MSB of address +2732 11AA D1 POP DE ; Restore string name +2733 11AB C5 PUSH BC ; Save address of string +2734 11AC F5 PUSH AF ; Save length of string +2735 11AD CD 27 16 CALL GSTRDE ; Get second string +2736 11B0 CD 38 1A CALL LOADFP ; Get address of second string +2737 11B3 F1 POP AF ; Restore length of string 1 +2738 11B4 57 LD D,A ; Length to D +2739 11B5 E1 POP HL ; Restore address of string 1 +2740 11B6 7B CMPSTR: LD A,E ; Bytes of string 2 to do +2741 11B7 B2 OR D ; Bytes of string 1 to do +2742 11B8 C8 RET Z ; Exit if all bytes compared +2743 11B9 7A LD A,D ; Get bytes of string 1 to do +2744 11BA D6 01 SUB 1 +2745 11BC D8 RET C ; Exit if end of string 1 +2746 11BD AF XOR A +2747 11BE BB CP E ; Bytes of string 2 to do +2748 11BF 3C INC A +2749 11C0 D0 RET NC ; Exit if end of string 2 +2750 11C1 15 DEC D ; Count bytes in string 1 +2751 11C2 1D DEC E ; Count bytes in string 2 +2752 11C3 0A LD A,(BC) ; Byte in string 2 +2753 11C4 BE CP (HL) ; Compare to byte in string 1 +2754 11C5 23 INC HL ; Move up string 1 +2755 11C6 03 INC BC ; Move up string 2 +2756 11C7 CA B6 11 JP Z,CMPSTR ; Same - Try next bytes +2757 11CA 3F CCF ; Flag difference (">" or "<") +2758 11CB C3 F4 19 JP FLGDIF ; "<" gives -1 , ">" gives +1 +2759 11CE +2760 11CE 3C CMPRES: INC A ; Increment current value +2761 11CF 8F ADC A,A ; Double plus carry +2762 11D0 C1 POP BC ; Get other value +2763 11D1 A0 AND B ; Combine them +2764 11D2 C6 FF ADD A,-1 ; Carry set if different +2765 11D4 9F SBC A,A ; 00 - Equal , FF - Different +2766 11D5 C3 FB 19 JP FLGREL ; Set current value & continue +2767 11D8 +2768 11D8 16 5A EVNOT: LD D,5AH ; Precedence value for "NOT" +2769 11DA CD 1C 10 CALL EVAL1 ; Eval until precedence break +2770 11DD CD 0A 10 CALL TSTNUM ; Make sure it's a number +2771 11E0 CD 4C 0C CALL DEINT ; Get integer -32768 - 32767 +2772 11E3 7B LD A,E ; Get LSB +2773 11E4 2F CPL ; Invert LSB +2774 11E5 4F LD C,A ; Save "NOT" of LSB +2775 11E6 7A LD A,D ; Get MSB +2776 11E7 2F CPL ; Invert MSB +2777 11E8 CD C1 13 CALL ACPASS ; Save AC as current +2778 11EB C1 POP BC ; Clean up stack +2779 11EC C3 28 10 JP EVAL3 ; Continue evaluation +2780 11EF +2781 11EF 2B DIMRET: DEC HL ; DEC 'cos GETCHR INCs +2782 11F0 CD 9A 0B CALL GETCHR ; Get next character +2783 11F3 C8 RET Z ; End of DIM statement +2784 11F4 CD 10 0A CALL CHKSYN ; Make sure ',' follows +2785 11F7 2C .BYTE ',' +2786 11F8 01 EF 11 DIM: LD BC,DIMRET ; Return to "DIMRET" +2787 11FB C5 PUSH BC ; Save on stack +2788 11FC F6 .BYTE 0F6H ; Flag "Create" variable +2789 11FD AF GETVAR: XOR A ; Find variable address,to DE +2790 11FE 32 5C 31 LD (LCRFLG),A ; Set locate / create flag +2791 1201 46 LD B,(HL) ; Get First byte of name +2792 1202 CD 38 0C GTFNAM: CALL CHKLTR ; See if a letter +2793 1205 DA 42 07 JP C,SNERR ; ?SN Error if not a letter +2794 1208 AF XOR A +2795 1209 4F LD C,A ; Clear second byte of name +2796 120A 32 5D 31 LD (TYPE),A ; Set type to numeric +2797 120D CD 9A 0B CALL GETCHR ; Get next character +2798 1210 DA 19 12 JP C,SVNAM2 ; Numeric - Save in name +2799 1213 CD 38 0C CALL CHKLTR ; See if a letter +2800 1216 DA 26 12 JP C,CHARTY ; Not a letter - Check type +2801 1219 4F SVNAM2: LD C,A ; Save second byte of name +2802 121A CD 9A 0B ENDNAM: CALL GETCHR ; Get next character +2803 121D DA 1A 12 JP C,ENDNAM ; Numeric - Get another +2804 1220 CD 38 0C CALL CHKLTR ; See if a letter +2805 1223 D2 1A 12 JP NC,ENDNAM ; Letter - Get another +2806 1226 D6 24 CHARTY: SUB '$' ; String variable? +2807 1228 C2 35 12 JP NZ,NOTSTR ; No - Numeric variable +2808 122B 3C INC A ; A = 1 (string type) +2809 122C 32 5D 31 LD (TYPE),A ; Set type to string +2810 122F 0F RRCA ; A = 80H , Flag for string +2811 1230 81 ADD A,C ; 2nd byte of name has bit 7 on +2812 1231 4F LD C,A ; Resave second byte on name +2813 1232 CD 9A 0B CALL GETCHR ; Get next character +2814 1235 3A 7B 31 NOTSTR: LD A,(FORFLG) ; Array name needed ? +2815 1238 3D DEC A +2816 1239 CA E2 12 JP Z,ARLDSV ; Yes - Get array name +2817 123C F2 45 12 JP P,NSCFOR ; No array with "FOR" or "FN" +2818 123F 7E LD A,(HL) ; Get byte again +2819 1240 D6 28 SUB '(' ; Subscripted variable? +2820 1242 CA BA 12 JP Z,SBSCPT ; Yes - Sort out subscript +2821 1245 +2822 1245 AF NSCFOR: XOR A ; Simple variable +2823 1246 32 7B 31 LD (FORFLG),A ; Clear "FOR" flag +2824 1249 E5 PUSH HL ; Save code string address +2825 124A 50 LD D,B ; DE = Variable name to find +2826 124B 59 LD E,C +2827 124C 2A 8E 31 LD HL,(FNRGNM) ; FN argument name +2828 124F CD 0A 0A CALL CPDEHL ; Is it the FN argument? +2829 1252 11 90 31 LD DE,FNARG ; Point to argument value +2830 1255 CA 2A 19 JP Z,POPHRT ; Yes - Return FN argument value +2831 1258 2A 88 31 LD HL,(VAREND) ; End of variables +2832 125B EB EX DE,HL ; Address of end of search +2833 125C 2A 86 31 LD HL,(PROGND) ; Start of variables address +2834 125F CD 0A 0A FNDVAR: CALL CPDEHL ; End of variable list table? +2835 1262 CA 78 12 JP Z,CFEVAL ; Yes - Called from EVAL? +2836 1265 79 LD A,C ; Get second byte of name +2837 1266 96 SUB (HL) ; Compare with name in list +2838 1267 23 INC HL ; Move on to first byte +2839 1268 C2 6D 12 JP NZ,FNTHR ; Different - Find another +2840 126B 78 LD A,B ; Get first byte of name +2841 126C 96 SUB (HL) ; Compare with name in list +2842 126D 23 FNTHR: INC HL ; Move on to LSB of value +2843 126E CA AC 12 JP Z,RETADR ; Found - Return address +2844 1271 23 INC HL ; <- Skip +2845 1272 23 INC HL ; <- over +2846 1273 23 INC HL ; <- F.P. +2847 1274 23 INC HL ; <- value +2848 1275 C3 5F 12 JP FNDVAR ; Keep looking +2849 1278 +2850 1278 E1 CFEVAL: POP HL ; Restore code string address +2851 1279 E3 EX (SP),HL ; Get return address +2852 127A D5 PUSH DE ; Save address of variable +2853 127B 11 FA 10 LD DE,FRMEVL ; Return address in EVAL +2854 127E CD 0A 0A CALL CPDEHL ; Called from EVAL ? +2855 1281 D1 POP DE ; Restore address of variable +2856 1282 CA AF 12 JP Z,RETNUL ; Yes - Return null variable +2857 1285 E3 EX (SP),HL ; Put back return +2858 1286 E5 PUSH HL ; Save code string address +2859 1287 C5 PUSH BC ; Save variable name +2860 1288 01 06 00 LD BC,6 ; 2 byte name plus 4 byte data +2861 128B 2A 8A 31 LD HL,(ARREND) ; End of arrays +2862 128E E5 PUSH HL ; Save end of arrays +2863 128F 09 ADD HL,BC ; Move up 6 bytes +2864 1290 C1 POP BC ; Source address in BC +2865 1291 E5 PUSH HL ; Save new end address +2866 1292 CD 0E 07 CALL MOVUP ; Move arrays up +2867 1295 E1 POP HL ; Restore new end address +2868 1296 22 8A 31 LD (ARREND),HL ; Set new end address +2869 1299 60 LD H,B ; End of variables to HL +2870 129A 69 LD L,C +2871 129B 22 88 31 LD (VAREND),HL ; Set new end address +2872 129E +2873 129E 2B ZEROLP: DEC HL ; Back through to zero variable +2874 129F 36 00 LD (HL),0 ; Zero byte in variable +2875 12A1 CD 0A 0A CALL CPDEHL ; Done them all? +2876 12A4 C2 9E 12 JP NZ,ZEROLP ; No - Keep on going +2877 12A7 D1 POP DE ; Get variable name +2878 12A8 73 LD (HL),E ; Store second character +2879 12A9 23 INC HL +2880 12AA 72 LD (HL),D ; Store first character +2881 12AB 23 INC HL +2882 12AC EB RETADR: EX DE,HL ; Address of variable in DE +2883 12AD E1 POP HL ; Restore code string address +2884 12AE C9 RET +2885 12AF +2886 12AF 32 97 31 RETNUL: LD (FPEXP),A ; Set result to zero +2887 12B2 21 DE 06 LD HL,ZERBYT ; Also set a null string +2888 12B5 22 94 31 LD (FPREG),HL ; Save for EVAL +2889 12B8 E1 POP HL ; Restore code string address +2890 12B9 C9 RET +2891 12BA +2892 12BA E5 SBSCPT: PUSH HL ; Save code string address +2893 12BB 2A 5C 31 LD HL,(LCRFLG) ; Locate/Create and Type +2894 12BE E3 EX (SP),HL ; Save and get code string +2895 12BF 57 LD D,A ; Zero number of dimensions +2896 12C0 D5 SCPTLP: PUSH DE ; Save number of dimensions +2897 12C1 C5 PUSH BC ; Save array name +2898 12C2 CD 40 0C CALL FPSINT ; Get subscript (0-32767) +2899 12C5 C1 POP BC ; Restore array name +2900 12C6 F1 POP AF ; Get number of dimensions +2901 12C7 EB EX DE,HL +2902 12C8 E3 EX (SP),HL ; Save subscript value +2903 12C9 E5 PUSH HL ; Save LCRFLG and TYPE +2904 12CA EB EX DE,HL +2905 12CB 3C INC A ; Count dimensions +2906 12CC 57 LD D,A ; Save in D +2907 12CD 7E LD A,(HL) ; Get next byte in code string +2908 12CE FE 2C CP ',' ; Comma (more to come)? +2909 12D0 CA C0 12 JP Z,SCPTLP ; Yes - More subscripts +2910 12D3 CD 10 0A CALL CHKSYN ; Make sure ")" follows +2911 12D6 29 .BYTE ")" +2912 12D7 22 80 31 LD (NXTOPR),HL ; Save code string address +2913 12DA E1 POP HL ; Get LCRFLG and TYPE +2914 12DB 22 5C 31 LD (LCRFLG),HL ; Restore Locate/create & type +2915 12DE 1E 00 LD E,0 ; Flag not CSAVE* or CLOAD* +2916 12E0 D5 PUSH DE ; Save number of dimensions (D) +2917 12E1 11 .BYTE 11H ; Skip "PUSH HL" and "PUSH AF' +2918 12E2 +2919 12E2 E5 ARLDSV: PUSH HL ; Save code string address +2920 12E3 F5 PUSH AF ; A = 00 , Flags set = Z,N +2921 12E4 2A 88 31 LD HL,(VAREND) ; Start of arrays +2922 12E7 3E .BYTE 3EH ; Skip "ADD HL,DE" +2923 12E8 19 FNDARY: ADD HL,DE ; Move to next array start +2924 12E9 EB EX DE,HL +2925 12EA 2A 8A 31 LD HL,(ARREND) ; End of arrays +2926 12ED EB EX DE,HL ; Current array pointer +2927 12EE CD 0A 0A CALL CPDEHL ; End of arrays found? +2928 12F1 CA 1A 13 JP Z,CREARY ; Yes - Create array +2929 12F4 7E LD A,(HL) ; Get second byte of name +2930 12F5 B9 CP C ; Compare with name given +2931 12F6 23 INC HL ; Move on +2932 12F7 C2 FC 12 JP NZ,NXTARY ; Different - Find next array +2933 12FA 7E LD A,(HL) ; Get first byte of name +2934 12FB B8 CP B ; Compare with name given +2935 12FC 23 NXTARY: INC HL ; Move on +2936 12FD 5E LD E,(HL) ; Get LSB of next array address +2937 12FE 23 INC HL +2938 12FF 56 LD D,(HL) ; Get MSB of next array address +2939 1300 23 INC HL +2940 1301 C2 E8 12 JP NZ,FNDARY ; Not found - Keep looking +2941 1304 3A 5C 31 LD A,(LCRFLG) ; Found Locate or Create it? +2942 1307 B7 OR A +2943 1308 C2 4B 07 JP NZ,DDERR ; Create - ?DD Error +2944 130B F1 POP AF ; Locate - Get number of dim'ns +2945 130C 44 LD B,H ; BC Points to array dim'ns +2946 130D 4D LD C,L +2947 130E CA 2A 19 JP Z,POPHRT ; Jump if array load/save +2948 1311 96 SUB (HL) ; Same number of dimensions? +2949 1312 CA 78 13 JP Z,FINDEL ; Yes - Find element +2950 1315 1E 10 BSERR: LD E,BS ; ?BS Error +2951 1317 C3 56 07 JP ERROR ; Output error +2952 131A +2953 131A 11 04 00 CREARY: LD DE,4 ; 4 Bytes per entry +2954 131D F1 POP AF ; Array to save or 0 dim'ns? +2955 131E CA 61 0C JP Z,FCERR ; Yes - ?FC Error +2956 1321 71 LD (HL),C ; Save second byte of name +2957 1322 23 INC HL +2958 1323 70 LD (HL),B ; Save first byte of name +2959 1324 23 INC HL +2960 1325 4F LD C,A ; Number of dimensions to C +2961 1326 CD 1F 07 CALL CHKSTK ; Check if enough memory +2962 1329 23 INC HL ; Point to number of dimensions +2963 132A 23 INC HL +2964 132B 22 75 31 LD (CUROPR),HL ; Save address of pointer +2965 132E 71 LD (HL),C ; Set number of dimensions +2966 132F 23 INC HL +2967 1330 3A 5C 31 LD A,(LCRFLG) ; Locate of Create? +2968 1333 17 RLA ; Carry set = Create +2969 1334 79 LD A,C ; Get number of dimensions +2970 1335 01 0B 00 CRARLP: LD BC,10+1 ; Default dimension size 10 +2971 1338 D2 3D 13 JP NC,DEFSIZ ; Locate - Set default size +2972 133B C1 POP BC ; Get specified dimension size +2973 133C 03 INC BC ; Include zero element +2974 133D 71 DEFSIZ: LD (HL),C ; Save LSB of dimension size +2975 133E 23 INC HL +2976 133F 70 LD (HL),B ; Save MSB of dimension size +2977 1340 23 INC HL +2978 1341 F5 PUSH AF ; Save num' of dim'ns an status +2979 1342 E5 PUSH HL ; Save address of dim'n size +2980 1343 CD D5 1A CALL MLDEBC ; Multiply DE by BC to find +2981 1346 EB EX DE,HL ; amount of mem needed (to DE) +2982 1347 E1 POP HL ; Restore address of dimension +2983 1348 F1 POP AF ; Restore number of dimensions +2984 1349 3D DEC A ; Count them +2985 134A C2 35 13 JP NZ,CRARLP ; Do next dimension if more +2986 134D F5 PUSH AF ; Save locate/create flag +2987 134E 42 LD B,D ; MSB of memory needed +2988 134F 4B LD C,E ; LSB of memory needed +2989 1350 EB EX DE,HL +2990 1351 19 ADD HL,DE ; Add bytes to array start +2991 1352 DA 37 07 JP C,OMERR ; Too big - Error +2992 1355 CD 28 07 CALL ENFMEM ; See if enough memory +2993 1358 22 8A 31 LD (ARREND),HL ; Save new end of array +2994 135B +2995 135B 2B ZERARY: DEC HL ; Back through array data +2996 135C 36 00 LD (HL),0 ; Set array element to zero +2997 135E CD 0A 0A CALL CPDEHL ; All elements zeroed? +2998 1361 C2 5B 13 JP NZ,ZERARY ; No - Keep on going +2999 1364 03 INC BC ; Number of bytes + 1 +3000 1365 57 LD D,A ; A=0 +3001 1366 2A 75 31 LD HL,(CUROPR) ; Get address of array +3002 1369 5E LD E,(HL) ; Number of dimensions +3003 136A EB EX DE,HL ; To HL +3004 136B 29 ADD HL,HL ; Two bytes per dimension size +3005 136C 09 ADD HL,BC ; Add number of bytes +3006 136D EB EX DE,HL ; Bytes needed to DE +3007 136E 2B DEC HL +3008 136F 2B DEC HL +3009 1370 73 LD (HL),E ; Save LSB of bytes needed +3010 1371 23 INC HL +3011 1372 72 LD (HL),D ; Save MSB of bytes needed +3012 1373 23 INC HL +3013 1374 F1 POP AF ; Locate / Create? +3014 1375 DA 9C 13 JP C,ENDDIM ; A is 0 , End if create +3015 1378 47 FINDEL: LD B,A ; Find array element +3016 1379 4F LD C,A +3017 137A 7E LD A,(HL) ; Number of dimensions +3018 137B 23 INC HL +3019 137C 16 .BYTE 16H ; Skip "POP HL" +3020 137D E1 FNDELP: POP HL ; Address of next dim' size +3021 137E 5E LD E,(HL) ; Get LSB of dim'n size +3022 137F 23 INC HL +3023 1380 56 LD D,(HL) ; Get MSB of dim'n size +3024 1381 23 INC HL +3025 1382 E3 EX (SP),HL ; Save address - Get index +3026 1383 F5 PUSH AF ; Save number of dim'ns +3027 1384 CD 0A 0A CALL CPDEHL ; Dimension too large? +3028 1387 D2 15 13 JP NC,BSERR ; Yes - ?BS Error +3029 138A E5 PUSH HL ; Save index +3030 138B CD D5 1A CALL MLDEBC ; Multiply previous by size +3031 138E D1 POP DE ; Index supplied to DE +3032 138F 19 ADD HL,DE ; Add index to pointer +3033 1390 F1 POP AF ; Number of dimensions +3034 1391 3D DEC A ; Count them +3035 1392 44 LD B,H ; MSB of pointer +3036 1393 4D LD C,L ; LSB of pointer +3037 1394 C2 7D 13 JP NZ,FNDELP ; More - Keep going +3038 1397 29 ADD HL,HL ; 4 Bytes per element +3039 1398 29 ADD HL,HL +3040 1399 C1 POP BC ; Start of array +3041 139A 09 ADD HL,BC ; Point to element +3042 139B EB EX DE,HL ; Address of element to DE +3043 139C 2A 80 31 ENDDIM: LD HL,(NXTOPR) ; Got code string address +3044 139F C9 RET +3045 13A0 +3046 13A0 2A 8A 31 FRE: LD HL,(ARREND) ; Start of free memory +3047 13A3 EB EX DE,HL ; To DE +3048 13A4 21 00 00 LD HL,0 ; End of free memory +3049 13A7 39 ADD HL,SP ; Current stack value +3050 13A8 3A 5D 31 LD A,(TYPE) ; Dummy argument type +3051 13AB B7 OR A +3052 13AC CA BC 13 JP Z,FRENUM ; Numeric - Free variable space +3053 13AF CD 23 16 CALL GSTRCU ; Current string to pool +3054 13B2 CD 23 15 CALL GARBGE ; Garbage collection +3055 13B5 2A 0A 31 LD HL,(STRSPC) ; Bottom of string space in use +3056 13B8 EB EX DE,HL ; To DE +3057 13B9 2A 73 31 LD HL,(STRBOT) ; Bottom of string space +3058 13BC 7D FRENUM: LD A,L ; Get LSB of end +3059 13BD 93 SUB E ; Subtract LSB of beginning +3060 13BE 4F LD C,A ; Save difference if C +3061 13BF 7C LD A,H ; Get MSB of end +3062 13C0 9A SBC A,D ; Subtract MSB of beginning +3063 13C1 41 ACPASS: LD B,C ; Return integer AC +3064 13C2 50 ABPASS: LD D,B ; Return integer AB +3065 13C3 1E 00 LD E,0 +3066 13C5 21 5D 31 LD HL,TYPE ; Point to type +3067 13C8 73 LD (HL),E ; Set type to numeric +3068 13C9 06 90 LD B,80H+16 ; 16 bit integer +3069 13CB C3 00 1A JP RETINT ; Return the integr +3070 13CE +3071 13CE 3A 5B 31 POS: LD A,(CURPOS) ; Get cursor position +3072 13D1 47 PASSA: LD B,A ; Put A into AB +3073 13D2 AF XOR A ; Zero A +3074 13D3 C3 C2 13 JP ABPASS ; Return integer AB +3075 13D6 +3076 13D6 CD 59 14 DEF: CALL CHEKFN ; Get "FN" and name +3077 13D9 CD 4B 14 CALL IDTEST ; Test for illegal direct +3078 13DC 01 31 0D LD BC,DATA ; To get next statement +3079 13DF C5 PUSH BC ; Save address for RETurn +3080 13E0 D5 PUSH DE ; Save address of function ptr +3081 13E1 CD 10 0A CALL CHKSYN ; Make sure "(" follows +3082 13E4 28 .BYTE "(" +3083 13E5 CD FD 11 CALL GETVAR ; Get argument variable name +3084 13E8 E5 PUSH HL ; Save code string address +3085 13E9 EB EX DE,HL ; Argument address to HL +3086 13EA 2B DEC HL +3087 13EB 56 LD D,(HL) ; Get first byte of arg name +3088 13EC 2B DEC HL +3089 13ED 5E LD E,(HL) ; Get second byte of arg name +3090 13EE E1 POP HL ; Restore code string address +3091 13EF CD 0A 10 CALL TSTNUM ; Make sure numeric argument +3092 13F2 CD 10 0A CALL CHKSYN ; Make sure ")" follows +3093 13F5 29 .BYTE ")" +3094 13F6 CD 10 0A CALL CHKSYN ; Make sure "=" follows +3095 13F9 B4 .BYTE ZEQUAL ; "=" token +3096 13FA 44 LD B,H ; Code string address to BC +3097 13FB 4D LD C,L +3098 13FC E3 EX (SP),HL ; Save code str , Get FN ptr +3099 13FD 71 LD (HL),C ; Save LSB of FN code string +3100 13FE 23 INC HL +3101 13FF 70 LD (HL),B ; Save MSB of FN code string +3102 1400 C3 98 14 JP SVSTAD ; Save address and do function +3103 1403 +3104 1403 CD 59 14 DOFN: CALL CHEKFN ; Make sure FN follows +3105 1406 D5 PUSH DE ; Save function pointer address +3106 1407 CD DE 10 CALL EVLPAR ; Evaluate expression in "()" +3107 140A CD 0A 10 CALL TSTNUM ; Make sure numeric result +3108 140D E3 EX (SP),HL ; Save code str , Get FN ptr +3109 140E 5E LD E,(HL) ; Get LSB of FN code string +3110 140F 23 INC HL +3111 1410 56 LD D,(HL) ; Get MSB of FN code string +3112 1411 23 INC HL +3113 1412 7A LD A,D ; And function DEFined? +3114 1413 B3 OR E +3115 1414 CA 4E 07 JP Z,UFERR ; No - ?UF Error +3116 1417 7E LD A,(HL) ; Get LSB of argument address +3117 1418 23 INC HL +3118 1419 66 LD H,(HL) ; Get MSB of argument address +3119 141A 6F LD L,A ; HL = Arg variable address +3120 141B E5 PUSH HL ; Save it +3121 141C 2A 8E 31 LD HL,(FNRGNM) ; Get old argument name +3122 141F E3 EX (SP),HL ; ; Save old , Get new +3123 1420 22 8E 31 LD (FNRGNM),HL ; Set new argument name +3124 1423 2A 92 31 LD HL,(FNARG+2) ; Get LSB,NLSB of old arg value +3125 1426 E5 PUSH HL ; Save it +3126 1427 2A 90 31 LD HL,(FNARG) ; Get MSB,EXP of old arg value +3127 142A E5 PUSH HL ; Save it +3128 142B 21 90 31 LD HL,FNARG ; HL = Value of argument +3129 142E D5 PUSH DE ; Save FN code string address +3130 142F CD 41 1A CALL FPTHL ; Move FPREG to argument +3131 1432 E1 POP HL ; Get FN code string address +3132 1433 CD 07 10 CALL GETNUM ; Get value from function +3133 1436 2B DEC HL ; DEC 'cos GETCHR INCs +3134 1437 CD 9A 0B CALL GETCHR ; Get next character +3135 143A C2 42 07 JP NZ,SNERR ; Bad character in FN - Error +3136 143D E1 POP HL ; Get MSB,EXP of old arg +3137 143E 22 90 31 LD (FNARG),HL ; Restore it +3138 1441 E1 POP HL ; Get LSB,NLSB of old arg +3139 1442 22 92 31 LD (FNARG+2),HL ; Restore it +3140 1445 E1 POP HL ; Get name of old arg +3141 1446 22 8E 31 LD (FNRGNM),HL ; Restore it +3142 1449 E1 POP HL ; Restore code string address +3143 144A C9 RET +3144 144B +3145 144B E5 IDTEST: PUSH HL ; Save code string address +3146 144C 2A 0C 31 LD HL,(LINEAT) ; Get current line number +3147 144F 23 INC HL ; -1 means direct statement +3148 1450 7C LD A,H +3149 1451 B5 OR L +3150 1452 E1 POP HL ; Restore code string address +3151 1453 C0 RET NZ ; Return if in program +3152 1454 1E 16 LD E,ID ; ?ID Error +3153 1456 C3 56 07 JP ERROR +3154 1459 +3155 1459 CD 10 0A CHEKFN: CALL CHKSYN ; Make sure FN follows +3156 145C A7 .BYTE ZFN ; "FN" token +3157 145D 3E 80 LD A,80H +3158 145F 32 7B 31 LD (FORFLG),A ; Flag FN name to find +3159 1462 B6 OR (HL) ; FN name has bit 7 set +3160 1463 47 LD B,A ; in first byte of name +3161 1464 CD 02 12 CALL GTFNAM ; Get FN name +3162 1467 C3 0A 10 JP TSTNUM ; Make sure numeric function +3163 146A +3164 146A CD 0A 10 STR: CALL TSTNUM ; Make sure it's a number +3165 146D CD 8E 1B CALL NUMASC ; Turn number into text +3166 1470 CD 9E 14 STR1: CALL CRTST ; Create string entry for it +3167 1473 CD 23 16 CALL GSTRCU ; Current string to pool +3168 1476 01 7E 16 LD BC,TOPOOL ; Save in string pool +3169 1479 C5 PUSH BC ; Save address on stack +3170 147A +3171 147A 7E SAVSTR: LD A,(HL) ; Get string length +3172 147B 23 INC HL +3173 147C 23 INC HL +3174 147D E5 PUSH HL ; Save pointer to string +3175 147E CD F9 14 CALL TESTR ; See if enough string space +3176 1481 E1 POP HL ; Restore pointer to string +3177 1482 4E LD C,(HL) ; Get LSB of address +3178 1483 23 INC HL +3179 1484 46 LD B,(HL) ; Get MSB of address +3180 1485 CD 92 14 CALL CRTMST ; Create string entry +3181 1488 E5 PUSH HL ; Save pointer to MSB of addr +3182 1489 6F LD L,A ; Length of string +3183 148A CD 16 16 CALL TOSTRA ; Move to string area +3184 148D D1 POP DE ; Restore pointer to MSB +3185 148E C9 RET +3186 148F +3187 148F CD F9 14 MKTMST: CALL TESTR ; See if enough string space +3188 1492 21 6F 31 CRTMST: LD HL,TMPSTR ; Temporary string +3189 1495 E5 PUSH HL ; Save it +3190 1496 77 LD (HL),A ; Save length of string +3191 1497 23 INC HL +3192 1498 23 SVSTAD: INC HL +3193 1499 73 LD (HL),E ; Save LSB of address +3194 149A 23 INC HL +3195 149B 72 LD (HL),D ; Save MSB of address +3196 149C E1 POP HL ; Restore pointer +3197 149D C9 RET +3198 149E +3199 149E 2B CRTST: DEC HL ; DEC - INCed after +3200 149F 06 22 QTSTR: LD B,'"' ; Terminating quote +3201 14A1 50 LD D,B ; Quote to D +3202 14A2 E5 DTSTR: PUSH HL ; Save start +3203 14A3 0E FF LD C,-1 ; Set counter to -1 +3204 14A5 23 QTSTLP: INC HL ; Move on +3205 14A6 7E LD A,(HL) ; Get byte +3206 14A7 0C INC C ; Count bytes +3207 14A8 B7 OR A ; End of line? +3208 14A9 CA B4 14 JP Z,CRTSTE ; Yes - Create string entry +3209 14AC BA CP D ; Terminator D found? +3210 14AD CA B4 14 JP Z,CRTSTE ; Yes - Create string entry +3211 14B0 B8 CP B ; Terminator B found? +3212 14B1 C2 A5 14 JP NZ,QTSTLP ; No - Keep looking +3213 14B4 FE 22 CRTSTE: CP '"' ; End with '"'? +3214 14B6 CC 9A 0B CALL Z,GETCHR ; Yes - Get next character +3215 14B9 E3 EX (SP),HL ; Starting quote +3216 14BA 23 INC HL ; First byte of string +3217 14BB EB EX DE,HL ; To DE +3218 14BC 79 LD A,C ; Get length +3219 14BD CD 92 14 CALL CRTMST ; Create string entry +3220 14C0 11 6F 31 TSTOPL: LD DE,TMPSTR ; Temporary string +3221 14C3 2A 61 31 LD HL,(TMSTPT) ; Temporary string pool pointer +3222 14C6 22 94 31 LD (FPREG),HL ; Save address of string ptr +3223 14C9 3E 01 LD A,1 +3224 14CB 32 5D 31 LD (TYPE),A ; Set type to string +3225 14CE CD 44 1A CALL DETHL4 ; Move string to pool +3226 14D1 CD 0A 0A CALL CPDEHL ; Out of string pool? +3227 14D4 22 61 31 LD (TMSTPT),HL ; Save new pointer +3228 14D7 E1 POP HL ; Restore code string address +3229 14D8 7E LD A,(HL) ; Get next code byte +3230 14D9 C0 RET NZ ; Return if pool OK +3231 14DA 1E 1E LD E,ST ; ?ST Error +3232 14DC C3 56 07 JP ERROR ; String pool overflow +3233 14DF +3234 14DF 23 PRNUMS: INC HL ; Skip leading space +3235 14E0 CD 9E 14 PRS: CALL CRTST ; Create string entry for it +3236 14E3 CD 23 16 PRS1: CALL GSTRCU ; Current string to pool +3237 14E6 CD 38 1A CALL LOADFP ; Move string block to BCDE +3238 14E9 1C INC E ; Length + 1 +3239 14EA 1D PRSLP: DEC E ; Count characters +3240 14EB C8 RET Z ; End of string +3241 14EC 0A LD A,(BC) ; Get byte to output +3242 14ED CD 1B 0A CALL OUTC ; Output character in A +3243 14F0 FE 0D CP CR ; Return? +3244 14F2 CC 4C 0E CALL Z,DONULL ; Yes - Do nulls +3245 14F5 03 INC BC ; Next byte in string +3246 14F6 C3 EA 14 JP PRSLP ; More characters to output +3247 14F9 +3248 14F9 B7 TESTR: OR A ; Test if enough room +3249 14FA 0E .BYTE 0EH ; No garbage collection done +3250 14FB F1 GRBDON: POP AF ; Garbage collection done +3251 14FC F5 PUSH AF ; Save status +3252 14FD 2A 0A 31 LD HL,(STRSPC) ; Bottom of string space in use +3253 1500 EB EX DE,HL ; To DE +3254 1501 2A 73 31 LD HL,(STRBOT) ; Bottom of string area +3255 1504 2F CPL ; Negate length (Top down) +3256 1505 4F LD C,A ; -Length to BC +3257 1506 06 FF LD B,-1 ; BC = -ve length of string +3258 1508 09 ADD HL,BC ; Add to bottom of space in use +3259 1509 23 INC HL ; Plus one for 2's complement +3260 150A CD 0A 0A CALL CPDEHL ; Below string RAM area? +3261 150D DA 17 15 JP C,TESTOS ; Tidy up if not done else err +3262 1510 22 73 31 LD (STRBOT),HL ; Save new bottom of area +3263 1513 23 INC HL ; Point to first byte of string +3264 1514 EB EX DE,HL ; Address to DE +3265 1515 F1 POPAF: POP AF ; Throw away status push +3266 1516 C9 RET +3267 1517 +3268 1517 F1 TESTOS: POP AF ; Garbage collect been done? +3269 1518 1E 1A LD E,OS ; ?OS Error +3270 151A CA 56 07 JP Z,ERROR ; Yes - Not enough string apace +3271 151D BF CP A ; Flag garbage collect done +3272 151E F5 PUSH AF ; Save status +3273 151F 01 FB 14 LD BC,GRBDON ; Garbage collection done +3274 1522 C5 PUSH BC ; Save for RETurn +3275 1523 2A 5F 31 GARBGE: LD HL,(LSTRAM) ; Get end of RAM pointer +3276 1526 22 73 31 GARBLP: LD (STRBOT),HL ; Reset string pointer +3277 1529 21 00 00 LD HL,0 +3278 152C E5 PUSH HL ; Flag no string found +3279 152D 2A 0A 31 LD HL,(STRSPC) ; Get bottom of string space +3280 1530 E5 PUSH HL ; Save bottom of string space +3281 1531 21 63 31 LD HL,TMSTPL ; Temporary string pool +3282 1534 EB GRBLP: EX DE,HL +3283 1535 2A 61 31 LD HL,(TMSTPT) ; Temporary string pool pointer +3284 1538 EB EX DE,HL +3285 1539 CD 0A 0A CALL CPDEHL ; Temporary string pool done? +3286 153C 01 34 15 LD BC,GRBLP ; Loop until string pool done +3287 153F C2 88 15 JP NZ,STPOOL ; No - See if in string area +3288 1542 2A 86 31 LD HL,(PROGND) ; Start of simple variables +3289 1545 EB SMPVAR: EX DE,HL +3290 1546 2A 88 31 LD HL,(VAREND) ; End of simple variables +3291 1549 EB EX DE,HL +3292 154A CD 0A 0A CALL CPDEHL ; All simple strings done? +3293 154D CA 5B 15 JP Z,ARRLP ; Yes - Do string arrays +3294 1550 7E LD A,(HL) ; Get type of variable +3295 1551 23 INC HL +3296 1552 23 INC HL +3297 1553 B7 OR A ; "S" flag set if string +3298 1554 CD 8B 15 CALL STRADD ; See if string in string area +3299 1557 C3 45 15 JP SMPVAR ; Loop until simple ones done +3300 155A +3301 155A C1 GNXARY: POP BC ; Scrap address of this array +3302 155B EB ARRLP: EX DE,HL +3303 155C 2A 8A 31 LD HL,(ARREND) ; End of string arrays +3304 155F EB EX DE,HL +3305 1560 CD 0A 0A CALL CPDEHL ; All string arrays done? +3306 1563 CA B1 15 JP Z,SCNEND ; Yes - Move string if found +3307 1566 CD 38 1A CALL LOADFP ; Get array name to BCDE +3308 1569 7B LD A,E ; Get type of array +3309 156A E5 PUSH HL ; Save address of num of dim'ns +3310 156B 09 ADD HL,BC ; Start of next array +3311 156C B7 OR A ; Test type of array +3312 156D F2 5A 15 JP P,GNXARY ; Numeric array - Ignore it +3313 1570 22 75 31 LD (CUROPR),HL ; Save address of next array +3314 1573 E1 POP HL ; Get address of num of dim'ns +3315 1574 4E LD C,(HL) ; BC = Number of dimensions +3316 1575 06 00 LD B,0 +3317 1577 09 ADD HL,BC ; Two bytes per dimension size +3318 1578 09 ADD HL,BC +3319 1579 23 INC HL ; Plus one for number of dim'ns +3320 157A EB GRBARY: EX DE,HL +3321 157B 2A 75 31 LD HL,(CUROPR) ; Get address of next array +3322 157E EB EX DE,HL +3323 157F CD 0A 0A CALL CPDEHL ; Is this array finished? +3324 1582 CA 5B 15 JP Z,ARRLP ; Yes - Get next one +3325 1585 01 7A 15 LD BC,GRBARY ; Loop until array all done +3326 1588 C5 STPOOL: PUSH BC ; Save return address +3327 1589 F6 80 OR 80H ; Flag string type +3328 158B 7E STRADD: LD A,(HL) ; Get string length +3329 158C 23 INC HL +3330 158D 23 INC HL +3331 158E 5E LD E,(HL) ; Get LSB of string address +3332 158F 23 INC HL +3333 1590 56 LD D,(HL) ; Get MSB of string address +3334 1591 23 INC HL +3335 1592 F0 RET P ; Not a string - Return +3336 1593 B7 OR A ; Set flags on string length +3337 1594 C8 RET Z ; Null string - Return +3338 1595 44 LD B,H ; Save variable pointer +3339 1596 4D LD C,L +3340 1597 2A 73 31 LD HL,(STRBOT) ; Bottom of new area +3341 159A CD 0A 0A CALL CPDEHL ; String been done? +3342 159D 60 LD H,B ; Restore variable pointer +3343 159E 69 LD L,C +3344 159F D8 RET C ; String done - Ignore +3345 15A0 E1 POP HL ; Return address +3346 15A1 E3 EX (SP),HL ; Lowest available string area +3347 15A2 CD 0A 0A CALL CPDEHL ; String within string area? +3348 15A5 E3 EX (SP),HL ; Lowest available string area +3349 15A6 E5 PUSH HL ; Re-save return address +3350 15A7 60 LD H,B ; Restore variable pointer +3351 15A8 69 LD L,C +3352 15A9 D0 RET NC ; Outside string area - Ignore +3353 15AA C1 POP BC ; Get return , Throw 2 away +3354 15AB F1 POP AF ; +3355 15AC F1 POP AF ; +3356 15AD E5 PUSH HL ; Save variable pointer +3357 15AE D5 PUSH DE ; Save address of current +3358 15AF C5 PUSH BC ; Put back return address +3359 15B0 C9 RET ; Go to it +3360 15B1 +3361 15B1 D1 SCNEND: POP DE ; Addresses of strings +3362 15B2 E1 POP HL ; +3363 15B3 7D LD A,L ; HL = 0 if no more to do +3364 15B4 B4 OR H +3365 15B5 C8 RET Z ; No more to do - Return +3366 15B6 2B DEC HL +3367 15B7 46 LD B,(HL) ; MSB of address of string +3368 15B8 2B DEC HL +3369 15B9 4E LD C,(HL) ; LSB of address of string +3370 15BA E5 PUSH HL ; Save variable address +3371 15BB 2B DEC HL +3372 15BC 2B DEC HL +3373 15BD 6E LD L,(HL) ; HL = Length of string +3374 15BE 26 00 LD H,0 +3375 15C0 09 ADD HL,BC ; Address of end of string+1 +3376 15C1 50 LD D,B ; String address to DE +3377 15C2 59 LD E,C +3378 15C3 2B DEC HL ; Last byte in string +3379 15C4 44 LD B,H ; Address to BC +3380 15C5 4D LD C,L +3381 15C6 2A 73 31 LD HL,(STRBOT) ; Current bottom of string area +3382 15C9 CD 11 07 CALL MOVSTR ; Move string to new address +3383 15CC E1 POP HL ; Restore variable address +3384 15CD 71 LD (HL),C ; Save new LSB of address +3385 15CE 23 INC HL +3386 15CF 70 LD (HL),B ; Save new MSB of address +3387 15D0 69 LD L,C ; Next string area+1 to HL +3388 15D1 60 LD H,B +3389 15D2 2B DEC HL ; Next string area address +3390 15D3 C3 26 15 JP GARBLP ; Look for more strings +3391 15D6 +3392 15D6 C5 CONCAT: PUSH BC ; Save prec' opr & code string +3393 15D7 E5 PUSH HL ; +3394 15D8 2A 94 31 LD HL,(FPREG) ; Get first string +3395 15DB E3 EX (SP),HL ; Save first string +3396 15DC CD 90 10 CALL OPRND ; Get second string +3397 15DF E3 EX (SP),HL ; Restore first string +3398 15E0 CD 0B 10 CALL TSTSTR ; Make sure it's a string +3399 15E3 7E LD A,(HL) ; Get length of second string +3400 15E4 E5 PUSH HL ; Save first string +3401 15E5 2A 94 31 LD HL,(FPREG) ; Get second string +3402 15E8 E5 PUSH HL ; Save second string +3403 15E9 86 ADD A,(HL) ; Add length of second string +3404 15EA 1E 1C LD E,LS ; ?LS Error +3405 15EC DA 56 07 JP C,ERROR ; String too long - Error +3406 15EF CD 8F 14 CALL MKTMST ; Make temporary string +3407 15F2 D1 POP DE ; Get second string to DE +3408 15F3 CD 27 16 CALL GSTRDE ; Move to string pool if needed +3409 15F6 E3 EX (SP),HL ; Get first string +3410 15F7 CD 26 16 CALL GSTRHL ; Move to string pool if needed +3411 15FA E5 PUSH HL ; Save first string +3412 15FB 2A 71 31 LD HL,(TMPSTR+2) ; Temporary string address +3413 15FE EB EX DE,HL ; To DE +3414 15FF CD 0D 16 CALL SSTSA ; First string to string area +3415 1602 CD 0D 16 CALL SSTSA ; Second string to string area +3416 1605 21 25 10 LD HL,EVAL2 ; Return to evaluation loop +3417 1608 E3 EX (SP),HL ; Save return,get code string +3418 1609 E5 PUSH HL ; Save code string address +3419 160A C3 C0 14 JP TSTOPL ; To temporary string to pool +3420 160D +3421 160D E1 SSTSA: POP HL ; Return address +3422 160E E3 EX (SP),HL ; Get string block,save return +3423 160F 7E LD A,(HL) ; Get length of string +3424 1610 23 INC HL +3425 1611 23 INC HL +3426 1612 4E LD C,(HL) ; Get LSB of string address +3427 1613 23 INC HL +3428 1614 46 LD B,(HL) ; Get MSB of string address +3429 1615 6F LD L,A ; Length to L +3430 1616 2C TOSTRA: INC L ; INC - DECed after +3431 1617 2D TSALP: DEC L ; Count bytes moved +3432 1618 C8 RET Z ; End of string - Return +3433 1619 0A LD A,(BC) ; Get source +3434 161A 12 LD (DE),A ; Save destination +3435 161B 03 INC BC ; Next source +3436 161C 13 INC DE ; Next destination +3437 161D C3 17 16 JP TSALP ; Loop until string moved +3438 1620 +3439 1620 CD 0B 10 GETSTR: CALL TSTSTR ; Make sure it's a string +3440 1623 2A 94 31 GSTRCU: LD HL,(FPREG) ; Get current string +3441 1626 EB GSTRHL: EX DE,HL ; Save DE +3442 1627 CD 41 16 GSTRDE: CALL BAKTMP ; Was it last tmp-str? +3443 162A EB EX DE,HL ; Restore DE +3444 162B C0 RET NZ ; No - Return +3445 162C D5 PUSH DE ; Save string +3446 162D 50 LD D,B ; String block address to DE +3447 162E 59 LD E,C +3448 162F 1B DEC DE ; Point to length +3449 1630 4E LD C,(HL) ; Get string length +3450 1631 2A 73 31 LD HL,(STRBOT) ; Current bottom of string area +3451 1634 CD 0A 0A CALL CPDEHL ; Last one in string area? +3452 1637 C2 3F 16 JP NZ,POPHL ; No - Return +3453 163A 47 LD B,A ; Clear B (A=0) +3454 163B 09 ADD HL,BC ; Remove string from str' area +3455 163C 22 73 31 LD (STRBOT),HL ; Save new bottom of str' area +3456 163F E1 POPHL: POP HL ; Restore string +3457 1640 C9 RET +3458 1641 +3459 1641 2A 61 31 BAKTMP: LD HL,(TMSTPT) ; Get temporary string pool top +3460 1644 2B DEC HL ; Back +3461 1645 46 LD B,(HL) ; Get MSB of address +3462 1646 2B DEC HL ; Back +3463 1647 4E LD C,(HL) ; Get LSB of address +3464 1648 2B DEC HL ; Back +3465 1649 2B DEC HL ; Back +3466 164A CD 0A 0A CALL CPDEHL ; String last in string pool? +3467 164D C0 RET NZ ; Yes - Leave it +3468 164E 22 61 31 LD (TMSTPT),HL ; Save new string pool top +3469 1651 C9 RET +3470 1652 +3471 1652 01 D1 13 LEN: LD BC,PASSA ; To return integer A +3472 1655 C5 PUSH BC ; Save address +3473 1656 CD 20 16 GETLEN: CALL GETSTR ; Get string and its length +3474 1659 AF XOR A +3475 165A 57 LD D,A ; Clear D +3476 165B 32 5D 31 LD (TYPE),A ; Set type to numeric +3477 165E 7E LD A,(HL) ; Get length of string +3478 165F B7 OR A ; Set status flags +3479 1660 C9 RET +3480 1661 +3481 1661 01 D1 13 ASC: LD BC,PASSA ; To return integer A +3482 1664 C5 PUSH BC ; Save address +3483 1665 CD 56 16 GTFLNM: CALL GETLEN ; Get length of string +3484 1668 CA 61 0C JP Z,FCERR ; Null string - Error +3485 166B 23 INC HL +3486 166C 23 INC HL +3487 166D 5E LD E,(HL) ; Get LSB of address +3488 166E 23 INC HL +3489 166F 56 LD D,(HL) ; Get MSB of address +3490 1670 1A LD A,(DE) ; Get first byte of string +3491 1671 C9 RET +3492 1672 +3493 1672 3E 01 CHR: LD A,1 ; One character string +3494 1674 CD 8F 14 CALL MKTMST ; Make a temporary string +3495 1677 CD 6B 17 CALL MAKINT ; Make it integer A +3496 167A 2A 71 31 LD HL,(TMPSTR+2) ; Get address of string +3497 167D 73 LD (HL),E ; Save character +3498 167E C1 TOPOOL: POP BC ; Clean up stack +3499 167F C3 C0 14 JP TSTOPL ; Temporary string to pool +3500 1682 +3501 1682 CD 1B 17 LEFT: CALL LFRGNM ; Get number and ending ")" +3502 1685 AF XOR A ; Start at first byte in string +3503 1686 E3 RIGHT1: EX (SP),HL ; Save code string,Get string +3504 1687 4F LD C,A ; Starting position in string +3505 1688 E5 MID1: PUSH HL ; Save string block address +3506 1689 7E LD A,(HL) ; Get length of string +3507 168A B8 CP B ; Compare with number given +3508 168B DA 90 16 JP C,ALLFOL ; All following bytes required +3509 168E 78 LD A,B ; Get new length +3510 168F 11 .BYTE 11H ; Skip "LD C,0" +3511 1690 0E 00 ALLFOL: LD C,0 ; First byte of string +3512 1692 C5 PUSH BC ; Save position in string +3513 1693 CD F9 14 CALL TESTR ; See if enough string space +3514 1696 C1 POP BC ; Get position in string +3515 1697 E1 POP HL ; Restore string block address +3516 1698 E5 PUSH HL ; And re-save it +3517 1699 23 INC HL +3518 169A 23 INC HL +3519 169B 46 LD B,(HL) ; Get LSB of address +3520 169C 23 INC HL +3521 169D 66 LD H,(HL) ; Get MSB of address +3522 169E 68 LD L,B ; HL = address of string +3523 169F 06 00 LD B,0 ; BC = starting address +3524 16A1 09 ADD HL,BC ; Point to that byte +3525 16A2 44 LD B,H ; BC = source string +3526 16A3 4D LD C,L +3527 16A4 CD 92 14 CALL CRTMST ; Create a string entry +3528 16A7 6F LD L,A ; Length of new string +3529 16A8 CD 16 16 CALL TOSTRA ; Move string to string area +3530 16AB D1 POP DE ; Clear stack +3531 16AC CD 27 16 CALL GSTRDE ; Move to string pool if needed +3532 16AF C3 C0 14 JP TSTOPL ; Temporary string to pool +3533 16B2 +3534 16B2 CD 1B 17 RIGHT: CALL LFRGNM ; Get number and ending ")" +3535 16B5 D1 POP DE ; Get string length +3536 16B6 D5 PUSH DE ; And re-save +3537 16B7 1A LD A,(DE) ; Get length +3538 16B8 90 SUB B ; Move back N bytes +3539 16B9 C3 86 16 JP RIGHT1 ; Go and get sub-string +3540 16BC +3541 16BC EB MID: EX DE,HL ; Get code string address +3542 16BD 7E LD A,(HL) ; Get next byte ',' or ")" +3543 16BE CD 20 17 CALL MIDNUM ; Get number supplied +3544 16C1 04 INC B ; Is it character zero? +3545 16C2 05 DEC B +3546 16C3 CA 61 0C JP Z,FCERR ; Yes - Error +3547 16C6 C5 PUSH BC ; Save starting position +3548 16C7 1E FF LD E,255 ; All of string +3549 16C9 FE 29 CP ')' ; Any length given? +3550 16CB CA D5 16 JP Z,RSTSTR ; No - Rest of string +3551 16CE CD 10 0A CALL CHKSYN ; Make sure ',' follows +3552 16D1 2C .BYTE ',' +3553 16D2 CD 68 17 CALL GETINT ; Get integer 0-255 +3554 16D5 CD 10 0A RSTSTR: CALL CHKSYN ; Make sure ")" follows +3555 16D8 29 .BYTE ")" +3556 16D9 F1 POP AF ; Restore starting position +3557 16DA E3 EX (SP),HL ; Get string,8ave code string +3558 16DB 01 88 16 LD BC,MID1 ; Continuation of MID$ routine +3559 16DE C5 PUSH BC ; Save for return +3560 16DF 3D DEC A ; Starting position-1 +3561 16E0 BE CP (HL) ; Compare with length +3562 16E1 06 00 LD B,0 ; Zero bytes length +3563 16E3 D0 RET NC ; Null string if start past end +3564 16E4 4F LD C,A ; Save starting position-1 +3565 16E5 7E LD A,(HL) ; Get length of string +3566 16E6 91 SUB C ; Subtract start +3567 16E7 BB CP E ; Enough string for it? +3568 16E8 47 LD B,A ; Save maximum length available +3569 16E9 D8 RET C ; Truncate string if needed +3570 16EA 43 LD B,E ; Set specified length +3571 16EB C9 RET ; Go and create string +3572 16EC +3573 16EC CD 56 16 VAL: CALL GETLEN ; Get length of string +3574 16EF CA 09 18 JP Z,RESZER ; Result zero +3575 16F2 5F LD E,A ; Save length +3576 16F3 23 INC HL +3577 16F4 23 INC HL +3578 16F5 7E LD A,(HL) ; Get LSB of address +3579 16F6 23 INC HL +3580 16F7 66 LD H,(HL) ; Get MSB of address +3581 16F8 6F LD L,A ; HL = String address +3582 16F9 E5 PUSH HL ; Save string address +3583 16FA 19 ADD HL,DE +3584 16FB 46 LD B,(HL) ; Get end of string+1 byte +3585 16FC 72 LD (HL),D ; Zero it to terminate +3586 16FD E3 EX (SP),HL ; Save string end,get start +3587 16FE C5 PUSH BC ; Save end+1 byte +3588 16FF 7E LD A,(HL) ; Get starting byte +3589 1700 FE 24 CP '$' ; Hex number indicated? [function added] +3590 1702 C2 0A 17 JP NZ,VAL1 +3591 1705 CD 34 1F CALL HEXTFP ; Convert Hex to FPREG +3592 1708 18 0D JR VAL3 +3593 170A FE 25 VAL1: CP '%' ; Binary number indicated? [function added] +3594 170C C2 14 17 JP NZ,VAL2 +3595 170F CD A4 1F CALL BINTFP ; Convert Bin to FPREG +3596 1712 18 03 JR VAL3 +3597 1714 CD F0 1A VAL2: CALL ASCTFP ; Convert ASCII string to FP +3598 1717 C1 VAL3: POP BC ; Restore end+1 byte +3599 1718 E1 POP HL ; Restore end+1 address +3600 1719 70 LD (HL),B ; Put back original byte +3601 171A C9 RET +3602 171B +3603 171B EB LFRGNM: EX DE,HL ; Code string address to HL +3604 171C CD 10 0A CALL CHKSYN ; Make sure ")" follows +3605 171F 29 .BYTE ")" +3606 1720 C1 MIDNUM: POP BC ; Get return address +3607 1721 D1 POP DE ; Get number supplied +3608 1722 C5 PUSH BC ; Re-save return address +3609 1723 43 LD B,E ; Number to B +3610 1724 C9 RET +3611 1725 +3612 1725 CD 6B 17 INP: CALL MAKINT ; Make it integer A +3613 1728 32 EF 30 LD (INPORT),A ; Set input port +3614 172B CD EE 30 CALL INPSUB ; Get input from port +3615 172E C3 D1 13 JP PASSA ; Return integer A +3616 1731 +3617 1731 CD 55 17 POUT: CALL SETIO ; Set up port number +3618 1734 C3 B6 30 JP OUTSUB ; Output data and return +3619 1737 +3620 1737 CD 55 17 WAIT: CALL SETIO ; Set up port number +3621 173A F5 PUSH AF ; Save AND mask +3622 173B 1E 00 LD E,0 ; Assume zero if none given +3623 173D 2B DEC HL ; DEC 'cos GETCHR INCs +3624 173E CD 9A 0B CALL GETCHR ; Get next character +3625 1741 CA 4B 17 JP Z,NOXOR ; No XOR byte given +3626 1744 CD 10 0A CALL CHKSYN ; Make sure ',' follows +3627 1747 2C .BYTE ',' +3628 1748 CD 68 17 CALL GETINT ; Get integer 0-255 to XOR with +3629 174B C1 NOXOR: POP BC ; Restore AND mask +3630 174C CD EE 30 WAITLP: CALL INPSUB ; Get input +3631 174F AB XOR E ; Flip selected bits +3632 1750 A0 AND B ; Result non-zero? +3633 1751 CA 4C 17 JP Z,WAITLP ; No = keep waiting +3634 1754 C9 RET +3635 1755 +3636 1755 CD 68 17 SETIO: CALL GETINT ; Get integer 0-255 +3637 1758 32 EF 30 LD (INPORT),A ; Set input port +3638 175B 32 B7 30 LD (OTPORT),A ; Set output port +3639 175E CD 10 0A CALL CHKSYN ; Make sure ',' follows +3640 1761 2C .BYTE ',' +3641 1762 C3 68 17 JP GETINT ; Get integer 0-255 and return +3642 1765 +3643 1765 CD 9A 0B FNDNUM: CALL GETCHR ; Get next character +3644 1768 CD 07 10 GETINT: CALL GETNUM ; Get a number from 0 to 255 +3645 176B CD 46 0C MAKINT: CALL DEPINT ; Make sure value 0 - 255 +3646 176E 7A LD A,D ; Get MSB of number +3647 176F B7 OR A ; Zero? +3648 1770 C2 61 0C JP NZ,FCERR ; No - Error +3649 1773 2B DEC HL ; DEC 'cos GETCHR INCs +3650 1774 CD 9A 0B CALL GETCHR ; Get next character +3651 1777 7B LD A,E ; Get number to A +3652 1778 C9 RET +3653 1779 +3654 1779 CD 4C 0C PEEK: CALL DEINT ; Get memory address +3655 177C 1A LD A,(DE) ; Get byte in memory +3656 177D C3 D1 13 JP PASSA ; Return integer A +3657 1780 +3658 1780 CD 07 10 POKE: CALL GETNUM ; Get memory address +3659 1783 CD 4C 0C CALL DEINT ; Get integer -32768 to 3276 +3660 1786 D5 PUSH DE ; Save memory address +3661 1787 CD 10 0A CALL CHKSYN ; Make sure ',' follows +3662 178A 2C .BYTE ',' +3663 178B CD 68 17 CALL GETINT ; Get integer 0-255 +3664 178E D1 POP DE ; Restore memory address +3665 178F 12 LD (DE),A ; Load it into memory +3666 1790 C9 RET +3667 1791 +3668 1791 21 67 1C ROUND: LD HL,HALF ; Add 0.5 to FPREG +3669 1794 CD 38 1A ADDPHL: CALL LOADFP ; Load FP at (HL) to BCDE +3670 1797 C3 A3 17 JP FPADD ; Add BCDE to FPREG +3671 179A +3672 179A CD 38 1A SUBPHL: CALL LOADFP ; FPREG = -FPREG + number at HL +3673 179D 21 .BYTE 21H ; Skip "POP BC" and "POP DE" +3674 179E C1 PSUB: POP BC ; Get FP number from stack +3675 179F D1 POP DE +3676 17A0 CD 12 1A SUBCDE: CALL INVSGN ; Negate FPREG +3677 17A3 78 FPADD: LD A,B ; Get FP exponent +3678 17A4 B7 OR A ; Is number zero? +3679 17A5 C8 RET Z ; Yes - Nothing to add +3680 17A6 3A 97 31 LD A,(FPEXP) ; Get FPREG exponent +3681 17A9 B7 OR A ; Is this number zero? +3682 17AA CA 2A 1A JP Z,FPBCDE ; Yes - Move BCDE to FPREG +3683 17AD 90 SUB B ; BCDE number larger? +3684 17AE D2 BD 17 JP NC,NOSWAP ; No - Don't swap them +3685 17B1 2F CPL ; Two's complement +3686 17B2 3C INC A ; FP exponent +3687 17B3 EB EX DE,HL +3688 17B4 CD 1A 1A CALL STAKFP ; Put FPREG on stack +3689 17B7 EB EX DE,HL +3690 17B8 CD 2A 1A CALL FPBCDE ; Move BCDE to FPREG +3691 17BB C1 POP BC ; Restore number from stack +3692 17BC D1 POP DE +3693 17BD FE 19 NOSWAP: CP 24+1 ; Second number insignificant? +3694 17BF D0 RET NC ; Yes - First number is result +3695 17C0 F5 PUSH AF ; Save number of bits to scale +3696 17C1 CD 4F 1A CALL SIGNS ; Set MSBs & sign of result +3697 17C4 67 LD H,A ; Save sign of result +3698 17C5 F1 POP AF ; Restore scaling factor +3699 17C6 CD 68 18 CALL SCALE ; Scale BCDE to same exponent +3700 17C9 B4 OR H ; Result to be positive? +3701 17CA 21 94 31 LD HL,FPREG ; Point to FPREG +3702 17CD F2 E3 17 JP P,MINCDE ; No - Subtract FPREG from CDE +3703 17D0 CD 48 18 CALL PLUCDE ; Add FPREG to CDE +3704 17D3 D2 29 18 JP NC,RONDUP ; No overflow - Round it up +3705 17D6 23 INC HL ; Point to exponent +3706 17D7 34 INC (HL) ; Increment it +3707 17D8 CA 51 07 JP Z,OVERR ; Number overflowed - Error +3708 17DB 2E 01 LD L,1 ; 1 bit to shift right +3709 17DD CD 7E 18 CALL SHRT1 ; Shift result right +3710 17E0 C3 29 18 JP RONDUP ; Round it up +3711 17E3 +3712 17E3 AF MINCDE: XOR A ; Clear A and carry +3713 17E4 90 SUB B ; Negate exponent +3714 17E5 47 LD B,A ; Re-save exponent +3715 17E6 7E LD A,(HL) ; Get LSB of FPREG +3716 17E7 9B SBC A, E ; Subtract LSB of BCDE +3717 17E8 5F LD E,A ; Save LSB of BCDE +3718 17E9 23 INC HL +3719 17EA 7E LD A,(HL) ; Get NMSB of FPREG +3720 17EB 9A SBC A,D ; Subtract NMSB of BCDE +3721 17EC 57 LD D,A ; Save NMSB of BCDE +3722 17ED 23 INC HL +3723 17EE 7E LD A,(HL) ; Get MSB of FPREG +3724 17EF 99 SBC A,C ; Subtract MSB of BCDE +3725 17F0 4F LD C,A ; Save MSB of BCDE +3726 17F1 DC 54 18 CONPOS: CALL C,COMPL ; Overflow - Make it positive +3727 17F4 +3728 17F4 68 BNORM: LD L,B ; L = Exponent +3729 17F5 63 LD H,E ; H = LSB +3730 17F6 AF XOR A +3731 17F7 47 BNRMLP: LD B,A ; Save bit count +3732 17F8 79 LD A,C ; Get MSB +3733 17F9 B7 OR A ; Is it zero? +3734 17FA C2 16 18 JP NZ,PNORM ; No - Do it bit at a time +3735 17FD 4A LD C,D ; MSB = NMSB +3736 17FE 54 LD D,H ; NMSB= LSB +3737 17FF 65 LD H,L ; LSB = VLSB +3738 1800 6F LD L,A ; VLSB= 0 +3739 1801 78 LD A,B ; Get exponent +3740 1802 D6 08 SUB 8 ; Count 8 bits +3741 1804 FE E0 CP -24-8 ; Was number zero? +3742 1806 C2 F7 17 JP NZ,BNRMLP ; No - Keep normalising +3743 1809 AF RESZER: XOR A ; Result is zero +3744 180A 32 97 31 SAVEXP: LD (FPEXP),A ; Save result as zero +3745 180D C9 RET +3746 180E +3747 180E 05 NORMAL: DEC B ; Count bits +3748 180F 29 ADD HL,HL ; Shift HL left +3749 1810 7A LD A,D ; Get NMSB +3750 1811 17 RLA ; Shift left with last bit +3751 1812 57 LD D,A ; Save NMSB +3752 1813 79 LD A,C ; Get MSB +3753 1814 8F ADC A,A ; Shift left with last bit +3754 1815 4F LD C,A ; Save MSB +3755 1816 F2 0E 18 PNORM: JP P,NORMAL ; Not done - Keep going +3756 1819 78 LD A,B ; Number of bits shifted +3757 181A 5C LD E,H ; Save HL in EB +3758 181B 45 LD B,L +3759 181C B7 OR A ; Any shifting done? +3760 181D CA 29 18 JP Z,RONDUP ; No - Round it up +3761 1820 21 97 31 LD HL,FPEXP ; Point to exponent +3762 1823 86 ADD A,(HL) ; Add shifted bits +3763 1824 77 LD (HL),A ; Re-save exponent +3764 1825 D2 09 18 JP NC,RESZER ; Underflow - Result is zero +3765 1828 C8 RET Z ; Result is zero +3766 1829 78 RONDUP: LD A,B ; Get VLSB of number +3767 182A 21 97 31 RONDB: LD HL,FPEXP ; Point to exponent +3768 182D B7 OR A ; Any rounding? +3769 182E FC 3B 18 CALL M,FPROND ; Yes - Round number up +3770 1831 46 LD B,(HL) ; B = Exponent +3771 1832 23 INC HL +3772 1833 7E LD A,(HL) ; Get sign of result +3773 1834 E6 80 AND 10000000B ; Only bit 7 needed +3774 1836 A9 XOR C ; Set correct sign +3775 1837 4F LD C,A ; Save correct sign in number +3776 1838 C3 2A 1A JP FPBCDE ; Move BCDE to FPREG +3777 183B +3778 183B 1C FPROND: INC E ; Round LSB +3779 183C C0 RET NZ ; Return if ok +3780 183D 14 INC D ; Round NMSB +3781 183E C0 RET NZ ; Return if ok +3782 183F 0C INC C ; Round MSB +3783 1840 C0 RET NZ ; Return if ok +3784 1841 0E 80 LD C,80H ; Set normal value +3785 1843 34 INC (HL) ; Increment exponent +3786 1844 C0 RET NZ ; Return if ok +3787 1845 C3 51 07 JP OVERR ; Overflow error +3788 1848 +3789 1848 7E PLUCDE: LD A,(HL) ; Get LSB of FPREG +3790 1849 83 ADD A,E ; Add LSB of BCDE +3791 184A 5F LD E,A ; Save LSB of BCDE +3792 184B 23 INC HL +3793 184C 7E LD A,(HL) ; Get NMSB of FPREG +3794 184D 8A ADC A,D ; Add NMSB of BCDE +3795 184E 57 LD D,A ; Save NMSB of BCDE +3796 184F 23 INC HL +3797 1850 7E LD A,(HL) ; Get MSB of FPREG +3798 1851 89 ADC A,C ; Add MSB of BCDE +3799 1852 4F LD C,A ; Save MSB of BCDE +3800 1853 C9 RET +3801 1854 +3802 1854 21 98 31 COMPL: LD HL,SGNRES ; Sign of result +3803 1857 7E LD A,(HL) ; Get sign of result +3804 1858 2F CPL ; Negate it +3805 1859 77 LD (HL),A ; Put it back +3806 185A AF XOR A +3807 185B 6F LD L,A ; Set L to zero +3808 185C 90 SUB B ; Negate exponent,set carry +3809 185D 47 LD B,A ; Re-save exponent +3810 185E 7D LD A,L ; Load zero +3811 185F 9B SBC A,E ; Negate LSB +3812 1860 5F LD E,A ; Re-save LSB +3813 1861 7D LD A,L ; Load zero +3814 1862 9A SBC A,D ; Negate NMSB +3815 1863 57 LD D,A ; Re-save NMSB +3816 1864 7D LD A,L ; Load zero +3817 1865 99 SBC A,C ; Negate MSB +3818 1866 4F LD C,A ; Re-save MSB +3819 1867 C9 RET +3820 1868 +3821 1868 06 00 SCALE: LD B,0 ; Clear underflow +3822 186A D6 08 SCALLP: SUB 8 ; 8 bits (a whole byte)? +3823 186C DA 77 18 JP C,SHRITE ; No - Shift right A bits +3824 186F 43 LD B,E ; <- Shift +3825 1870 5A LD E,D ; <- right +3826 1871 51 LD D,C ; <- eight +3827 1872 0E 00 LD C,0 ; <- bits +3828 1874 C3 6A 18 JP SCALLP ; More bits to shift +3829 1877 +3830 1877 C6 09 SHRITE: ADD A,8+1 ; Adjust count +3831 1879 6F LD L,A ; Save bits to shift +3832 187A AF SHRLP: XOR A ; Flag for all done +3833 187B 2D DEC L ; All shifting done? +3834 187C C8 RET Z ; Yes - Return +3835 187D 79 LD A,C ; Get MSB +3836 187E 1F SHRT1: RRA ; Shift it right +3837 187F 4F LD C,A ; Re-save +3838 1880 7A LD A,D ; Get NMSB +3839 1881 1F RRA ; Shift right with last bit +3840 1882 57 LD D,A ; Re-save it +3841 1883 7B LD A,E ; Get LSB +3842 1884 1F RRA ; Shift right with last bit +3843 1885 5F LD E,A ; Re-save it +3844 1886 78 LD A,B ; Get underflow +3845 1887 1F RRA ; Shift right with last bit +3846 1888 47 LD B,A ; Re-save underflow +3847 1889 C3 7A 18 JP SHRLP ; More bits to do +3848 188C +3849 188C 00 00 00 81 UNITY: .BYTE 000H,000H,000H,081H ; 1.00000 +3850 1890 +3851 1890 03 LOGTAB: .BYTE 3 ; Table used by LOG +3852 1891 AA 56 19 80 .BYTE 0AAH,056H,019H,080H ; 0.59898 +3853 1895 F1 22 76 80 .BYTE 0F1H,022H,076H,080H ; 0.96147 +3854 1899 45 AA 38 82 .BYTE 045H,0AAH,038H,082H ; 2.88539 +3855 189D +3856 189D CD E9 19 LOG: CALL TSTSGN ; Test sign of value +3857 18A0 B7 OR A +3858 18A1 EA 61 0C JP PE,FCERR ; ?FC Error if <= zero +3859 18A4 21 97 31 LD HL,FPEXP ; Point to exponent +3860 18A7 7E LD A,(HL) ; Get exponent +3861 18A8 01 35 80 LD BC,8035H ; BCDE = SQR(1/2) +3862 18AB 11 F3 04 LD DE,04F3H +3863 18AE 90 SUB B ; Scale value to be < 1 +3864 18AF F5 PUSH AF ; Save scale factor +3865 18B0 70 LD (HL),B ; Save new exponent +3866 18B1 D5 PUSH DE ; Save SQR(1/2) +3867 18B2 C5 PUSH BC +3868 18B3 CD A3 17 CALL FPADD ; Add SQR(1/2) to value +3869 18B6 C1 POP BC ; Restore SQR(1/2) +3870 18B7 D1 POP DE +3871 18B8 04 INC B ; Make it SQR(2) +3872 18B9 CD 3F 19 CALL DVBCDE ; Divide by SQR(2) +3873 18BC 21 8C 18 LD HL,UNITY ; Point to 1. +3874 18BF CD 9A 17 CALL SUBPHL ; Subtract FPREG from 1 +3875 18C2 21 90 18 LD HL,LOGTAB ; Coefficient table +3876 18C5 CD 31 1D CALL SUMSER ; Evaluate sum of series +3877 18C8 01 80 80 LD BC,8080H ; BCDE = -0.5 +3878 18CB 11 00 00 LD DE,0000H +3879 18CE CD A3 17 CALL FPADD ; Subtract 0.5 from FPREG +3880 18D1 F1 POP AF ; Restore scale factor +3881 18D2 CD 64 1B CALL RSCALE ; Re-scale number +3882 18D5 01 31 80 MULLN2: LD BC,8031H ; BCDE = Ln(2) +3883 18D8 11 18 72 LD DE,7218H +3884 18DB 21 .BYTE 21H ; Skip "POP BC" and "POP DE" +3885 18DC +3886 18DC C1 MULT: POP BC ; Get number from stack +3887 18DD D1 POP DE +3888 18DE CD E9 19 FPMULT: CALL TSTSGN ; Test sign of FPREG +3889 18E1 C8 RET Z ; Return zero if zero +3890 18E2 2E 00 LD L,0 ; Flag add exponents +3891 18E4 CD A7 19 CALL ADDEXP ; Add exponents +3892 18E7 79 LD A,C ; Get MSB of multiplier +3893 18E8 32 A6 31 LD (MULVAL),A ; Save MSB of multiplier +3894 18EB EB EX DE,HL +3895 18EC 22 A7 31 LD (MULVAL+1),HL ; Save rest of multiplier +3896 18EF 01 00 00 LD BC,0 ; Partial product (BCDE) = zero +3897 18F2 50 LD D,B +3898 18F3 58 LD E,B +3899 18F4 21 F4 17 LD HL,BNORM ; Address of normalise +3900 18F7 E5 PUSH HL ; Save for return +3901 18F8 21 00 19 LD HL,MULT8 ; Address of 8 bit multiply +3902 18FB E5 PUSH HL ; Save for NMSB,MSB +3903 18FC E5 PUSH HL ; +3904 18FD 21 94 31 LD HL,FPREG ; Point to number +3905 1900 7E MULT8: LD A,(HL) ; Get LSB of number +3906 1901 23 INC HL ; Point to NMSB +3907 1902 B7 OR A ; Test LSB +3908 1903 CA 2C 19 JP Z,BYTSFT ; Zero - shift to next byte +3909 1906 E5 PUSH HL ; Save address of number +3910 1907 2E 08 LD L,8 ; 8 bits to multiply by +3911 1909 1F MUL8LP: RRA ; Shift LSB right +3912 190A 67 LD H,A ; Save LSB +3913 190B 79 LD A,C ; Get MSB +3914 190C D2 1A 19 JP NC,NOMADD ; Bit was zero - Don't add +3915 190F E5 PUSH HL ; Save LSB and count +3916 1910 2A A7 31 LD HL,(MULVAL+1) ; Get LSB and NMSB +3917 1913 19 ADD HL,DE ; Add NMSB and LSB +3918 1914 EB EX DE,HL ; Leave sum in DE +3919 1915 E1 POP HL ; Restore MSB and count +3920 1916 3A A6 31 LD A,(MULVAL) ; Get MSB of multiplier +3921 1919 89 ADC A,C ; Add MSB +3922 191A 1F NOMADD: RRA ; Shift MSB right +3923 191B 4F LD C,A ; Re-save MSB +3924 191C 7A LD A,D ; Get NMSB +3925 191D 1F RRA ; Shift NMSB right +3926 191E 57 LD D,A ; Re-save NMSB +3927 191F 7B LD A,E ; Get LSB +3928 1920 1F RRA ; Shift LSB right +3929 1921 5F LD E,A ; Re-save LSB +3930 1922 78 LD A,B ; Get VLSB +3931 1923 1F RRA ; Shift VLSB right +3932 1924 47 LD B,A ; Re-save VLSB +3933 1925 2D DEC L ; Count bits multiplied +3934 1926 7C LD A,H ; Get LSB of multiplier +3935 1927 C2 09 19 JP NZ,MUL8LP ; More - Do it +3936 192A E1 POPHRT: POP HL ; Restore address of number +3937 192B C9 RET +3938 192C +3939 192C 43 BYTSFT: LD B,E ; Shift partial product left +3940 192D 5A LD E,D +3941 192E 51 LD D,C +3942 192F 4F LD C,A +3943 1930 C9 RET +3944 1931 +3945 1931 CD 1A 1A DIV10: CALL STAKFP ; Save FPREG on stack +3946 1934 01 20 84 LD BC,8420H ; BCDE = 10. +3947 1937 11 00 00 LD DE,0000H +3948 193A CD 2A 1A CALL FPBCDE ; Move 10 to FPREG +3949 193D +3950 193D C1 DIV: POP BC ; Get number from stack +3951 193E D1 POP DE +3952 193F CD E9 19 DVBCDE: CALL TSTSGN ; Test sign of FPREG +3953 1942 CA 45 07 JP Z,DZERR ; Error if division by zero +3954 1945 2E FF LD L,-1 ; Flag subtract exponents +3955 1947 CD A7 19 CALL ADDEXP ; Subtract exponents +3956 194A 34 INC (HL) ; Add 2 to exponent to adjust +3957 194B 34 INC (HL) +3958 194C 2B DEC HL ; Point to MSB +3959 194D 7E LD A,(HL) ; Get MSB of dividend +3960 194E 32 C2 30 LD (DIV3),A ; Save for subtraction +3961 1951 2B DEC HL +3962 1952 7E LD A,(HL) ; Get NMSB of dividend +3963 1953 32 BE 30 LD (DIV2),A ; Save for subtraction +3964 1956 2B DEC HL +3965 1957 7E LD A,(HL) ; Get MSB of dividend +3966 1958 32 BA 30 LD (DIV1),A ; Save for subtraction +3967 195B 41 LD B,C ; Get MSB +3968 195C EB EX DE,HL ; NMSB,LSB to HL +3969 195D AF XOR A +3970 195E 4F LD C,A ; Clear MSB of quotient +3971 195F 57 LD D,A ; Clear NMSB of quotient +3972 1960 5F LD E,A ; Clear LSB of quotient +3973 1961 32 C5 30 LD (DIV4),A ; Clear overflow count +3974 1964 E5 DIVLP: PUSH HL ; Save divisor +3975 1965 C5 PUSH BC +3976 1966 7D LD A,L ; Get LSB of number +3977 1967 CD B9 30 CALL DIVSUP ; Subt' divisor from dividend +3978 196A DE 00 SBC A,0 ; Count for overflows +3979 196C 3F CCF +3980 196D D2 77 19 JP NC,RESDIV ; Restore divisor if borrow +3981 1970 32 C5 30 LD (DIV4),A ; Re-save overflow count +3982 1973 F1 POP AF ; Scrap divisor +3983 1974 F1 POP AF +3984 1975 37 SCF ; Set carry to +3985 1976 D2 .BYTE 0D2H ; Skip "POP BC" and "POP HL" +3986 1977 +3987 1977 C1 RESDIV: POP BC ; Restore divisor +3988 1978 E1 POP HL +3989 1979 79 LD A,C ; Get MSB of quotient +3990 197A 3C INC A +3991 197B 3D DEC A +3992 197C 1F RRA ; Bit 0 to bit 7 +3993 197D FA 2A 18 JP M,RONDB ; Done - Normalise result +3994 1980 17 RLA ; Restore carry +3995 1981 7B LD A,E ; Get LSB of quotient +3996 1982 17 RLA ; Double it +3997 1983 5F LD E,A ; Put it back +3998 1984 7A LD A,D ; Get NMSB of quotient +3999 1985 17 RLA ; Double it +4000 1986 57 LD D,A ; Put it back +4001 1987 79 LD A,C ; Get MSB of quotient +4002 1988 17 RLA ; Double it +4003 1989 4F LD C,A ; Put it back +4004 198A 29 ADD HL,HL ; Double NMSB,LSB of divisor +4005 198B 78 LD A,B ; Get MSB of divisor +4006 198C 17 RLA ; Double it +4007 198D 47 LD B,A ; Put it back +4008 198E 3A C5 30 LD A,(DIV4) ; Get VLSB of quotient +4009 1991 17 RLA ; Double it +4010 1992 32 C5 30 LD (DIV4),A ; Put it back +4011 1995 79 LD A,C ; Get MSB of quotient +4012 1996 B2 OR D ; Merge NMSB +4013 1997 B3 OR E ; Merge LSB +4014 1998 C2 64 19 JP NZ,DIVLP ; Not done - Keep dividing +4015 199B E5 PUSH HL ; Save divisor +4016 199C 21 97 31 LD HL,FPEXP ; Point to exponent +4017 199F 35 DEC (HL) ; Divide by 2 +4018 19A0 E1 POP HL ; Restore divisor +4019 19A1 C2 64 19 JP NZ,DIVLP ; Ok - Keep going +4020 19A4 C3 51 07 JP OVERR ; Overflow error +4021 19A7 +4022 19A7 78 ADDEXP: LD A,B ; Get exponent of dividend +4023 19A8 B7 OR A ; Test it +4024 19A9 CA CB 19 JP Z,OVTST3 ; Zero - Result zero +4025 19AC 7D LD A,L ; Get add/subtract flag +4026 19AD 21 97 31 LD HL,FPEXP ; Point to exponent +4027 19B0 AE XOR (HL) ; Add or subtract it +4028 19B1 80 ADD A,B ; Add the other exponent +4029 19B2 47 LD B,A ; Save new exponent +4030 19B3 1F RRA ; Test exponent for overflow +4031 19B4 A8 XOR B +4032 19B5 78 LD A,B ; Get exponent +4033 19B6 F2 CA 19 JP P,OVTST2 ; Positive - Test for overflow +4034 19B9 C6 80 ADD A,80H ; Add excess 128 +4035 19BB 77 LD (HL),A ; Save new exponent +4036 19BC CA 2A 19 JP Z,POPHRT ; Zero - Result zero +4037 19BF CD 4F 1A CALL SIGNS ; Set MSBs and sign of result +4038 19C2 77 LD (HL),A ; Save new exponent +4039 19C3 2B DEC HL ; Point to MSB +4040 19C4 C9 RET +4041 19C5 +4042 19C5 CD E9 19 OVTST1: CALL TSTSGN ; Test sign of FPREG +4043 19C8 2F CPL ; Invert sign +4044 19C9 E1 POP HL ; Clean up stack +4045 19CA B7 OVTST2: OR A ; Test if new exponent zero +4046 19CB E1 OVTST3: POP HL ; Clear off return address +4047 19CC F2 09 18 JP P,RESZER ; Result zero +4048 19CF C3 51 07 JP OVERR ; Overflow error +4049 19D2 +4050 19D2 CD 35 1A MLSP10: CALL BCDEFP ; Move FPREG to BCDE +4051 19D5 78 LD A,B ; Get exponent +4052 19D6 B7 OR A ; Is it zero? +4053 19D7 C8 RET Z ; Yes - Result is zero +4054 19D8 C6 02 ADD A,2 ; Multiply by 4 +4055 19DA DA 51 07 JP C,OVERR ; Overflow - ?OV Error +4056 19DD 47 LD B,A ; Re-save exponent +4057 19DE CD A3 17 CALL FPADD ; Add BCDE to FPREG (Times 5) +4058 19E1 21 97 31 LD HL,FPEXP ; Point to exponent +4059 19E4 34 INC (HL) ; Double number (Times 10) +4060 19E5 C0 RET NZ ; Ok - Return +4061 19E6 C3 51 07 JP OVERR ; Overflow error +4062 19E9 +4063 19E9 3A 97 31 TSTSGN: LD A,(FPEXP) ; Get sign of FPREG +4064 19EC B7 OR A +4065 19ED C8 RET Z ; RETurn if number is zero +4066 19EE 3A 96 31 LD A,(FPREG+2) ; Get MSB of FPREG +4067 19F1 FE .BYTE 0FEH ; Test sign +4068 19F2 2F RETREL: CPL ; Invert sign +4069 19F3 17 RLA ; Sign bit to carry +4070 19F4 9F FLGDIF: SBC A,A ; Carry to all bits of A +4071 19F5 C0 RET NZ ; Return -1 if negative +4072 19F6 3C INC A ; Bump to +1 +4073 19F7 C9 RET ; Positive - Return +1 +4074 19F8 +4075 19F8 CD E9 19 SGN: CALL TSTSGN ; Test sign of FPREG +4076 19FB 06 88 FLGREL: LD B,80H+8 ; 8 bit integer in exponent +4077 19FD 11 00 00 LD DE,0 ; Zero NMSB and LSB +4078 1A00 21 97 31 RETINT: LD HL,FPEXP ; Point to exponent +4079 1A03 4F LD C,A ; CDE = MSB,NMSB and LSB +4080 1A04 70 LD (HL),B ; Save exponent +4081 1A05 06 00 LD B,0 ; CDE = integer to normalise +4082 1A07 23 INC HL ; Point to sign of result +4083 1A08 36 80 LD (HL),80H ; Set sign of result +4084 1A0A 17 RLA ; Carry = sign of integer +4085 1A0B C3 F1 17 JP CONPOS ; Set sign of result +4086 1A0E +4087 1A0E CD E9 19 ABS: CALL TSTSGN ; Test sign of FPREG +4088 1A11 F0 RET P ; Return if positive +4089 1A12 21 96 31 INVSGN: LD HL,FPREG+2 ; Point to MSB +4090 1A15 7E LD A,(HL) ; Get sign of mantissa +4091 1A16 EE 80 XOR 80H ; Invert sign of mantissa +4092 1A18 77 LD (HL),A ; Re-save sign of mantissa +4093 1A19 C9 RET +4094 1A1A +4095 1A1A EB STAKFP: EX DE,HL ; Save code string address +4096 1A1B 2A 94 31 LD HL,(FPREG) ; LSB,NLSB of FPREG +4097 1A1E E3 EX (SP),HL ; Stack them,get return +4098 1A1F E5 PUSH HL ; Re-save return +4099 1A20 2A 96 31 LD HL,(FPREG+2) ; MSB and exponent of FPREG +4100 1A23 E3 EX (SP),HL ; Stack them,get return +4101 1A24 E5 PUSH HL ; Re-save return +4102 1A25 EB EX DE,HL ; Restore code string address +4103 1A26 C9 RET +4104 1A27 +4105 1A27 CD 38 1A PHLTFP: CALL LOADFP ; Number at HL to BCDE +4106 1A2A EB FPBCDE: EX DE,HL ; Save code string address +4107 1A2B 22 94 31 LD (FPREG),HL ; Save LSB,NLSB of number +4108 1A2E 60 LD H,B ; Exponent of number +4109 1A2F 69 LD L,C ; MSB of number +4110 1A30 22 96 31 LD (FPREG+2),HL ; Save MSB and exponent +4111 1A33 EB EX DE,HL ; Restore code string address +4112 1A34 C9 RET +4113 1A35 +4114 1A35 21 94 31 BCDEFP: LD HL,FPREG ; Point to FPREG +4115 1A38 5E LOADFP: LD E,(HL) ; Get LSB of number +4116 1A39 23 INC HL +4117 1A3A 56 LD D,(HL) ; Get NMSB of number +4118 1A3B 23 INC HL +4119 1A3C 4E LD C,(HL) ; Get MSB of number +4120 1A3D 23 INC HL +4121 1A3E 46 LD B,(HL) ; Get exponent of number +4122 1A3F 23 INCHL: INC HL ; Used for conditional "INC HL" +4123 1A40 C9 RET +4124 1A41 +4125 1A41 11 94 31 FPTHL: LD DE,FPREG ; Point to FPREG +4126 1A44 06 04 DETHL4: LD B,4 ; 4 bytes to move +4127 1A46 1A DETHLB: LD A,(DE) ; Get source +4128 1A47 77 LD (HL),A ; Save destination +4129 1A48 13 INC DE ; Next source +4130 1A49 23 INC HL ; Next destination +4131 1A4A 05 DEC B ; Count bytes +4132 1A4B C2 46 1A JP NZ,DETHLB ; Loop if more +4133 1A4E C9 RET +4134 1A4F +4135 1A4F 21 96 31 SIGNS: LD HL,FPREG+2 ; Point to MSB of FPREG +4136 1A52 7E LD A,(HL) ; Get MSB +4137 1A53 07 RLCA ; Old sign to carry +4138 1A54 37 SCF ; Set MSBit +4139 1A55 1F RRA ; Set MSBit of MSB +4140 1A56 77 LD (HL),A ; Save new MSB +4141 1A57 3F CCF ; Complement sign +4142 1A58 1F RRA ; Old sign to carry +4143 1A59 23 INC HL +4144 1A5A 23 INC HL +4145 1A5B 77 LD (HL),A ; Set sign of result +4146 1A5C 79 LD A,C ; Get MSB +4147 1A5D 07 RLCA ; Old sign to carry +4148 1A5E 37 SCF ; Set MSBit +4149 1A5F 1F RRA ; Set MSBit of MSB +4150 1A60 4F LD C,A ; Save MSB +4151 1A61 1F RRA +4152 1A62 AE XOR (HL) ; New sign of result +4153 1A63 C9 RET +4154 1A64 +4155 1A64 78 CMPNUM: LD A,B ; Get exponent of number +4156 1A65 B7 OR A +4157 1A66 CA E9 19 JP Z,TSTSGN ; Zero - Test sign of FPREG +4158 1A69 21 F2 19 LD HL,RETREL ; Return relation routine +4159 1A6C E5 PUSH HL ; Save for return +4160 1A6D CD E9 19 CALL TSTSGN ; Test sign of FPREG +4161 1A70 79 LD A,C ; Get MSB of number +4162 1A71 C8 RET Z ; FPREG zero - Number's MSB +4163 1A72 21 96 31 LD HL,FPREG+2 ; MSB of FPREG +4164 1A75 AE XOR (HL) ; Combine signs +4165 1A76 79 LD A,C ; Get MSB of number +4166 1A77 F8 RET M ; Exit if signs different +4167 1A78 CD 7E 1A CALL CMPFP ; Compare FP numbers +4168 1A7B 1F RRA ; Get carry to sign +4169 1A7C A9 XOR C ; Combine with MSB of number +4170 1A7D C9 RET +4171 1A7E +4172 1A7E 23 CMPFP: INC HL ; Point to exponent +4173 1A7F 78 LD A,B ; Get exponent +4174 1A80 BE CP (HL) ; Compare exponents +4175 1A81 C0 RET NZ ; Different +4176 1A82 2B DEC HL ; Point to MBS +4177 1A83 79 LD A,C ; Get MSB +4178 1A84 BE CP (HL) ; Compare MSBs +4179 1A85 C0 RET NZ ; Different +4180 1A86 2B DEC HL ; Point to NMSB +4181 1A87 7A LD A,D ; Get NMSB +4182 1A88 BE CP (HL) ; Compare NMSBs +4183 1A89 C0 RET NZ ; Different +4184 1A8A 2B DEC HL ; Point to LSB +4185 1A8B 7B LD A,E ; Get LSB +4186 1A8C 96 SUB (HL) ; Compare LSBs +4187 1A8D C0 RET NZ ; Different +4188 1A8E E1 POP HL ; Drop RETurn +4189 1A8F E1 POP HL ; Drop another RETurn +4190 1A90 C9 RET +4191 1A91 +4192 1A91 47 FPINT: LD B,A ; <- Move +4193 1A92 4F LD C,A ; <- exponent +4194 1A93 57 LD D,A ; <- to all +4195 1A94 5F LD E,A ; <- bits +4196 1A95 B7 OR A ; Test exponent +4197 1A96 C8 RET Z ; Zero - Return zero +4198 1A97 E5 PUSH HL ; Save pointer to number +4199 1A98 CD 35 1A CALL BCDEFP ; Move FPREG to BCDE +4200 1A9B CD 4F 1A CALL SIGNS ; Set MSBs & sign of result +4201 1A9E AE XOR (HL) ; Combine with sign of FPREG +4202 1A9F 67 LD H,A ; Save combined signs +4203 1AA0 FC B5 1A CALL M,DCBCDE ; Negative - Decrement BCDE +4204 1AA3 3E 98 LD A,80H+24 ; 24 bits +4205 1AA5 90 SUB B ; Bits to shift +4206 1AA6 CD 68 18 CALL SCALE ; Shift BCDE +4207 1AA9 7C LD A,H ; Get combined sign +4208 1AAA 17 RLA ; Sign to carry +4209 1AAB DC 3B 18 CALL C,FPROND ; Negative - Round number up +4210 1AAE 06 00 LD B,0 ; Zero exponent +4211 1AB0 DC 54 18 CALL C,COMPL ; If negative make positive +4212 1AB3 E1 POP HL ; Restore pointer to number +4213 1AB4 C9 RET +4214 1AB5 +4215 1AB5 1B DCBCDE: DEC DE ; Decrement BCDE +4216 1AB6 7A LD A,D ; Test LSBs +4217 1AB7 A3 AND E +4218 1AB8 3C INC A +4219 1AB9 C0 RET NZ ; Exit if LSBs not FFFF +4220 1ABA 0B DEC BC ; Decrement MSBs +4221 1ABB C9 RET +4222 1ABC +4223 1ABC 21 97 31 INT: LD HL,FPEXP ; Point to exponent +4224 1ABF 7E LD A,(HL) ; Get exponent +4225 1AC0 FE 98 CP 80H+24 ; Integer accuracy only? +4226 1AC2 3A 94 31 LD A,(FPREG) ; Get LSB +4227 1AC5 D0 RET NC ; Yes - Already integer +4228 1AC6 7E LD A,(HL) ; Get exponent +4229 1AC7 CD 91 1A CALL FPINT ; F.P to integer +4230 1ACA 36 98 LD (HL),80H+24 ; Save 24 bit integer +4231 1ACC 7B LD A,E ; Get LSB of number +4232 1ACD F5 PUSH AF ; Save LSB +4233 1ACE 79 LD A,C ; Get MSB of number +4234 1ACF 17 RLA ; Sign to carry +4235 1AD0 CD F1 17 CALL CONPOS ; Set sign of result +4236 1AD3 F1 POP AF ; Restore LSB of number +4237 1AD4 C9 RET +4238 1AD5 +4239 1AD5 21 00 00 MLDEBC: LD HL,0 ; Clear partial product +4240 1AD8 78 LD A,B ; Test multiplier +4241 1AD9 B1 OR C +4242 1ADA C8 RET Z ; Return zero if zero +4243 1ADB 3E 10 LD A,16 ; 16 bits +4244 1ADD 29 MLDBLP: ADD HL,HL ; Shift P.P left +4245 1ADE DA 15 13 JP C,BSERR ; ?BS Error if overflow +4246 1AE1 EB EX DE,HL +4247 1AE2 29 ADD HL,HL ; Shift multiplier left +4248 1AE3 EB EX DE,HL +4249 1AE4 D2 EB 1A JP NC,NOMLAD ; Bit was zero - No add +4250 1AE7 09 ADD HL,BC ; Add multiplicand +4251 1AE8 DA 15 13 JP C,BSERR ; ?BS Error if overflow +4252 1AEB 3D NOMLAD: DEC A ; Count bits +4253 1AEC C2 DD 1A JP NZ,MLDBLP ; More +4254 1AEF C9 RET +4255 1AF0 +4256 1AF0 FE 2D ASCTFP: CP '-' ; Negative? +4257 1AF2 F5 PUSH AF ; Save it and flags +4258 1AF3 CA FC 1A JP Z,CNVNUM ; Yes - Convert number +4259 1AF6 FE 2B CP '+' ; Positive? +4260 1AF8 CA FC 1A JP Z,CNVNUM ; Yes - Convert number +4261 1AFB 2B DEC HL ; DEC 'cos GETCHR INCs +4262 1AFC CD 09 18 CNVNUM: CALL RESZER ; Set result to zero +4263 1AFF 47 LD B,A ; Digits after point counter +4264 1B00 57 LD D,A ; Sign of exponent +4265 1B01 5F LD E,A ; Exponent of ten +4266 1B02 2F CPL +4267 1B03 4F LD C,A ; Before or after point flag +4268 1B04 CD 9A 0B MANLP: CALL GETCHR ; Get next character +4269 1B07 DA 4D 1B JP C,ADDIG ; Digit - Add to number +4270 1B0A FE 2E CP '.' +4271 1B0C CA 28 1B JP Z,DPOINT ; '.' - Flag point +4272 1B0F FE 45 CP 'E' +4273 1B11 C2 2C 1B JP NZ,CONEXP ; Not 'E' - Scale number +4274 1B14 CD 9A 0B CALL GETCHR ; Get next character +4275 1B17 CD 40 11 CALL SGNEXP ; Get sign of exponent +4276 1B1A CD 9A 0B EXPLP: CALL GETCHR ; Get next character +4277 1B1D DA 6F 1B JP C,EDIGIT ; Digit - Add to exponent +4278 1B20 14 INC D ; Is sign negative? +4279 1B21 C2 2C 1B JP NZ,CONEXP ; No - Scale number +4280 1B24 AF XOR A +4281 1B25 93 SUB E ; Negate exponent +4282 1B26 5F LD E,A ; And re-save it +4283 1B27 0C INC C ; Flag end of number +4284 1B28 0C DPOINT: INC C ; Flag point passed +4285 1B29 CA 04 1B JP Z,MANLP ; Zero - Get another digit +4286 1B2C E5 CONEXP: PUSH HL ; Save code string address +4287 1B2D 7B LD A,E ; Get exponent +4288 1B2E 90 SUB B ; Subtract digits after point +4289 1B2F F4 45 1B SCALMI: CALL P,SCALPL ; Positive - Multiply number +4290 1B32 F2 3B 1B JP P,ENDCON ; Positive - All done +4291 1B35 F5 PUSH AF ; Save number of times to /10 +4292 1B36 CD 31 19 CALL DIV10 ; Divide by 10 +4293 1B39 F1 POP AF ; Restore count +4294 1B3A 3C INC A ; Count divides +4295 1B3B +4296 1B3B C2 2F 1B ENDCON: JP NZ,SCALMI ; More to do +4297 1B3E D1 POP DE ; Restore code string address +4298 1B3F F1 POP AF ; Restore sign of number +4299 1B40 CC 12 1A CALL Z,INVSGN ; Negative - Negate number +4300 1B43 EB EX DE,HL ; Code string address to HL +4301 1B44 C9 RET +4302 1B45 +4303 1B45 C8 SCALPL: RET Z ; Exit if no scaling needed +4304 1B46 F5 MULTEN: PUSH AF ; Save count +4305 1B47 CD D2 19 CALL MLSP10 ; Multiply number by 10 +4306 1B4A F1 POP AF ; Restore count +4307 1B4B 3D DEC A ; Count multiplies +4308 1B4C C9 RET +4309 1B4D +4310 1B4D D5 ADDIG: PUSH DE ; Save sign of exponent +4311 1B4E 57 LD D,A ; Save digit +4312 1B4F 78 LD A,B ; Get digits after point +4313 1B50 89 ADC A,C ; Add one if after point +4314 1B51 47 LD B,A ; Re-save counter +4315 1B52 C5 PUSH BC ; Save point flags +4316 1B53 E5 PUSH HL ; Save code string address +4317 1B54 D5 PUSH DE ; Save digit +4318 1B55 CD D2 19 CALL MLSP10 ; Multiply number by 10 +4319 1B58 F1 POP AF ; Restore digit +4320 1B59 D6 30 SUB '0' ; Make it absolute +4321 1B5B CD 64 1B CALL RSCALE ; Re-scale number +4322 1B5E E1 POP HL ; Restore code string address +4323 1B5F C1 POP BC ; Restore point flags +4324 1B60 D1 POP DE ; Restore sign of exponent +4325 1B61 C3 04 1B JP MANLP ; Get another digit +4326 1B64 +4327 1B64 CD 1A 1A RSCALE: CALL STAKFP ; Put number on stack +4328 1B67 CD FB 19 CALL FLGREL ; Digit to add to FPREG +4329 1B6A C1 PADD: POP BC ; Restore number +4330 1B6B D1 POP DE +4331 1B6C C3 A3 17 JP FPADD ; Add BCDE to FPREG and return +4332 1B6F +4333 1B6F 7B EDIGIT: LD A,E ; Get digit +4334 1B70 07 RLCA ; Times 2 +4335 1B71 07 RLCA ; Times 4 +4336 1B72 83 ADD A,E ; Times 5 +4337 1B73 07 RLCA ; Times 10 +4338 1B74 86 ADD A,(HL) ; Add next digit +4339 1B75 D6 30 SUB '0' ; Make it absolute +4340 1B77 5F LD E,A ; Save new digit +4341 1B78 C3 1A 1B JP EXPLP ; Look for another digit +4342 1B7B +4343 1B7B E5 LINEIN: PUSH HL ; Save code string address +4344 1B7C 21 DA 06 LD HL,INMSG ; Output " in " +4345 1B7F CD E0 14 CALL PRS ; Output string at HL +4346 1B82 E1 POP HL ; Restore code string address +4347 1B83 EB PRNTHL: EX DE,HL ; Code string address to DE +4348 1B84 AF XOR A +4349 1B85 06 98 LD B,80H+24 ; 24 bits +4350 1B87 CD 00 1A CALL RETINT ; Return the integer +4351 1B8A 21 DF 14 LD HL,PRNUMS ; Print number string +4352 1B8D E5 PUSH HL ; Save for return +4353 1B8E 21 99 31 NUMASC: LD HL,PBUFF ; Convert number to ASCII +4354 1B91 E5 PUSH HL ; Save for return +4355 1B92 CD E9 19 CALL TSTSGN ; Test sign of FPREG +4356 1B95 36 20 LD (HL),' ' ; Space at start +4357 1B97 F2 9C 1B JP P,SPCFST ; Positive - Space to start +4358 1B9A 36 2D LD (HL),'-' ; '-' sign at start +4359 1B9C 23 SPCFST: INC HL ; First byte of number +4360 1B9D 36 30 LD (HL),'0' ; '0' if zero +4361 1B9F CA 52 1C JP Z,JSTZER ; Return '0' if zero +4362 1BA2 E5 PUSH HL ; Save buffer address +4363 1BA3 FC 12 1A CALL M,INVSGN ; Negate FPREG if negative +4364 1BA6 AF XOR A ; Zero A +4365 1BA7 F5 PUSH AF ; Save it +4366 1BA8 CD 58 1C CALL RNGTST ; Test number is in range +4367 1BAB 01 43 91 SIXDIG: LD BC,9143H ; BCDE - 99999.9 +4368 1BAE 11 F8 4F LD DE,4FF8H +4369 1BB1 CD 64 1A CALL CMPNUM ; Compare numbers +4370 1BB4 B7 OR A +4371 1BB5 E2 C9 1B JP PO,INRNG ; > 99999.9 - Sort it out +4372 1BB8 F1 POP AF ; Restore count +4373 1BB9 CD 46 1B CALL MULTEN ; Multiply by ten +4374 1BBC F5 PUSH AF ; Re-save count +4375 1BBD C3 AB 1B JP SIXDIG ; Test it again +4376 1BC0 +4377 1BC0 CD 31 19 GTSIXD: CALL DIV10 ; Divide by 10 +4378 1BC3 F1 POP AF ; Get count +4379 1BC4 3C INC A ; Count divides +4380 1BC5 F5 PUSH AF ; Re-save count +4381 1BC6 CD 58 1C CALL RNGTST ; Test number is in range +4382 1BC9 CD 91 17 INRNG: CALL ROUND ; Add 0.5 to FPREG +4383 1BCC 3C INC A +4384 1BCD CD 91 1A CALL FPINT ; F.P to integer +4385 1BD0 CD 2A 1A CALL FPBCDE ; Move BCDE to FPREG +4386 1BD3 01 06 03 LD BC,0306H ; 1E+06 to 1E-03 range +4387 1BD6 F1 POP AF ; Restore count +4388 1BD7 81 ADD A,C ; 6 digits before point +4389 1BD8 3C INC A ; Add one +4390 1BD9 FA E5 1B JP M,MAKNUM ; Do it in 'E' form if < 1E-02 +4391 1BDC FE 08 CP 6+1+1 ; More than 999999 ? +4392 1BDE D2 E5 1B JP NC,MAKNUM ; Yes - Do it in 'E' form +4393 1BE1 3C INC A ; Adjust for exponent +4394 1BE2 47 LD B,A ; Exponent of number +4395 1BE3 3E 02 LD A,2 ; Make it zero after +4396 1BE5 +4397 1BE5 3D MAKNUM: DEC A ; Adjust for digits to do +4398 1BE6 3D DEC A +4399 1BE7 E1 POP HL ; Restore buffer address +4400 1BE8 F5 PUSH AF ; Save count +4401 1BE9 11 6B 1C LD DE,POWERS ; Powers of ten +4402 1BEC 05 DEC B ; Count digits before point +4403 1BED C2 F6 1B JP NZ,DIGTXT ; Not zero - Do number +4404 1BF0 36 2E LD (HL),'.' ; Save point +4405 1BF2 23 INC HL ; Move on +4406 1BF3 36 30 LD (HL),'0' ; Save zero +4407 1BF5 23 INC HL ; Move on +4408 1BF6 05 DIGTXT: DEC B ; Count digits before point +4409 1BF7 36 2E LD (HL),'.' ; Save point in case +4410 1BF9 CC 3F 1A CALL Z,INCHL ; Last digit - move on +4411 1BFC C5 PUSH BC ; Save digits before point +4412 1BFD E5 PUSH HL ; Save buffer address +4413 1BFE D5 PUSH DE ; Save powers of ten +4414 1BFF CD 35 1A CALL BCDEFP ; Move FPREG to BCDE +4415 1C02 E1 POP HL ; Powers of ten table +4416 1C03 06 2F LD B, '0'-1 ; ASCII '0' - 1 +4417 1C05 04 TRYAGN: INC B ; Count subtractions +4418 1C06 7B LD A,E ; Get LSB +4419 1C07 96 SUB (HL) ; Subtract LSB +4420 1C08 5F LD E,A ; Save LSB +4421 1C09 23 INC HL +4422 1C0A 7A LD A,D ; Get NMSB +4423 1C0B 9E SBC A,(HL) ; Subtract NMSB +4424 1C0C 57 LD D,A ; Save NMSB +4425 1C0D 23 INC HL +4426 1C0E 79 LD A,C ; Get MSB +4427 1C0F 9E SBC A,(HL) ; Subtract MSB +4428 1C10 4F LD C,A ; Save MSB +4429 1C11 2B DEC HL ; Point back to start +4430 1C12 2B DEC HL +4431 1C13 D2 05 1C JP NC,TRYAGN ; No overflow - Try again +4432 1C16 CD 48 18 CALL PLUCDE ; Restore number +4433 1C19 23 INC HL ; Start of next number +4434 1C1A CD 2A 1A CALL FPBCDE ; Move BCDE to FPREG +4435 1C1D EB EX DE,HL ; Save point in table +4436 1C1E E1 POP HL ; Restore buffer address +4437 1C1F 70 LD (HL),B ; Save digit in buffer +4438 1C20 23 INC HL ; And move on +4439 1C21 C1 POP BC ; Restore digit count +4440 1C22 0D DEC C ; Count digits +4441 1C23 C2 F6 1B JP NZ,DIGTXT ; More - Do them +4442 1C26 05 DEC B ; Any decimal part? +4443 1C27 CA 36 1C JP Z,DOEBIT ; No - Do 'E' bit +4444 1C2A 2B SUPTLZ: DEC HL ; Move back through buffer +4445 1C2B 7E LD A,(HL) ; Get character +4446 1C2C FE 30 CP '0' ; '0' character? +4447 1C2E CA 2A 1C JP Z,SUPTLZ ; Yes - Look back for more +4448 1C31 FE 2E CP '.' ; A decimal point? +4449 1C33 C4 3F 1A CALL NZ,INCHL ; Move back over digit +4450 1C36 +4451 1C36 F1 DOEBIT: POP AF ; Get 'E' flag +4452 1C37 CA 55 1C JP Z,NOENED ; No 'E' needed - End buffer +4453 1C3A 36 45 LD (HL),'E' ; Put 'E' in buffer +4454 1C3C 23 INC HL ; And move on +4455 1C3D 36 2B LD (HL),'+' ; Put '+' in buffer +4456 1C3F F2 46 1C JP P,OUTEXP ; Positive - Output exponent +4457 1C42 36 2D LD (HL),'-' ; Put '-' in buffer +4458 1C44 2F CPL ; Negate exponent +4459 1C45 3C INC A +4460 1C46 06 2F OUTEXP: LD B,'0'-1 ; ASCII '0' - 1 +4461 1C48 04 EXPTEN: INC B ; Count subtractions +4462 1C49 D6 0A SUB 10 ; Tens digit +4463 1C4B D2 48 1C JP NC,EXPTEN ; More to do +4464 1C4E C6 3A ADD A,'0'+10 ; Restore and make ASCII +4465 1C50 23 INC HL ; Move on +4466 1C51 70 LD (HL),B ; Save MSB of exponent +4467 1C52 23 JSTZER: INC HL ; +4468 1C53 77 LD (HL),A ; Save LSB of exponent +4469 1C54 23 INC HL +4470 1C55 71 NOENED: LD (HL),C ; Mark end of buffer +4471 1C56 E1 POP HL ; Restore code string address +4472 1C57 C9 RET +4473 1C58 +4474 1C58 01 74 94 RNGTST: LD BC,9474H ; BCDE = 999999. +4475 1C5B 11 F7 23 LD DE,23F7H +4476 1C5E CD 64 1A CALL CMPNUM ; Compare numbers +4477 1C61 B7 OR A +4478 1C62 E1 POP HL ; Return address to HL +4479 1C63 E2 C0 1B JP PO,GTSIXD ; Too big - Divide by ten +4480 1C66 E9 JP (HL) ; Otherwise return to caller +4481 1C67 +4482 1C67 00 00 00 80 HALF: .BYTE 00H,00H,00H,80H ; 0.5 +4483 1C6B +4484 1C6B A0 86 01 POWERS: .BYTE 0A0H,086H,001H ; 100000 +4485 1C6E 10 27 00 .BYTE 010H,027H,000H ; 10000 +4486 1C71 E8 03 00 .BYTE 0E8H,003H,000H ; 1000 +4487 1C74 64 00 00 .BYTE 064H,000H,000H ; 100 +4488 1C77 0A 00 00 .BYTE 00AH,000H,000H ; 10 +4489 1C7A 01 00 00 .BYTE 001H,000H,000H ; 1 +4490 1C7D +4491 1C7D 21 12 1A NEGAFT: LD HL,INVSGN ; Negate result +4492 1C80 E3 EX (SP),HL ; To be done after caller +4493 1C81 E9 JP (HL) ; Return to caller +4494 1C82 +4495 1C82 CD 1A 1A SQR: CALL STAKFP ; Put value on stack +4496 1C85 21 67 1C LD HL,HALF ; Set power to 1/2 +4497 1C88 CD 27 1A CALL PHLTFP ; Move 1/2 to FPREG +4498 1C8B +4499 1C8B C1 POWER: POP BC ; Get base +4500 1C8C D1 POP DE +4501 1C8D CD E9 19 CALL TSTSGN ; Test sign of power +4502 1C90 78 LD A,B ; Get exponent of base +4503 1C91 CA D0 1C JP Z,EXP ; Make result 1 if zero +4504 1C94 F2 9B 1C JP P,POWER1 ; Positive base - Ok +4505 1C97 B7 OR A ; Zero to negative power? +4506 1C98 CA 45 07 JP Z,DZERR ; Yes - ?/0 Error +4507 1C9B B7 POWER1: OR A ; Base zero? +4508 1C9C CA 0A 18 JP Z,SAVEXP ; Yes - Return zero +4509 1C9F D5 PUSH DE ; Save base +4510 1CA0 C5 PUSH BC +4511 1CA1 79 LD A,C ; Get MSB of base +4512 1CA2 F6 7F OR 01111111B ; Get sign status +4513 1CA4 CD 35 1A CALL BCDEFP ; Move power to BCDE +4514 1CA7 F2 B8 1C JP P,POWER2 ; Positive base - Ok +4515 1CAA D5 PUSH DE ; Save power +4516 1CAB C5 PUSH BC +4517 1CAC CD BC 1A CALL INT ; Get integer of power +4518 1CAF C1 POP BC ; Restore power +4519 1CB0 D1 POP DE +4520 1CB1 F5 PUSH AF ; MSB of base +4521 1CB2 CD 64 1A CALL CMPNUM ; Power an integer? +4522 1CB5 E1 POP HL ; Restore MSB of base +4523 1CB6 7C LD A,H ; but don't affect flags +4524 1CB7 1F RRA ; Exponent odd or even? +4525 1CB8 E1 POWER2: POP HL ; Restore MSB and exponent +4526 1CB9 22 96 31 LD (FPREG+2),HL ; Save base in FPREG +4527 1CBC E1 POP HL ; LSBs of base +4528 1CBD 22 94 31 LD (FPREG),HL ; Save in FPREG +4529 1CC0 DC 7D 1C CALL C,NEGAFT ; Odd power - Negate result +4530 1CC3 CC 12 1A CALL Z,INVSGN ; Negative base - Negate it +4531 1CC6 D5 PUSH DE ; Save power +4532 1CC7 C5 PUSH BC +4533 1CC8 CD 9D 18 CALL LOG ; Get LOG of base +4534 1CCB C1 POP BC ; Restore power +4535 1CCC D1 POP DE +4536 1CCD CD DE 18 CALL FPMULT ; Multiply LOG by power +4537 1CD0 +4538 1CD0 CD 1A 1A EXP: CALL STAKFP ; Put value on stack +4539 1CD3 01 38 81 LD BC,08138H ; BCDE = 1/Ln(2) +4540 1CD6 11 3B AA LD DE,0AA3BH +4541 1CD9 CD DE 18 CALL FPMULT ; Multiply value by 1/LN(2) +4542 1CDC 3A 97 31 LD A,(FPEXP) ; Get exponent +4543 1CDF FE 88 CP 80H+8 ; Is it in range? +4544 1CE1 D2 C5 19 JP NC,OVTST1 ; No - Test for overflow +4545 1CE4 CD BC 1A CALL INT ; Get INT of FPREG +4546 1CE7 C6 80 ADD A,80H ; For excess 128 +4547 1CE9 C6 02 ADD A,2 ; Exponent > 126? +4548 1CEB DA C5 19 JP C,OVTST1 ; Yes - Test for overflow +4549 1CEE F5 PUSH AF ; Save scaling factor +4550 1CEF 21 8C 18 LD HL,UNITY ; Point to 1. +4551 1CF2 CD 94 17 CALL ADDPHL ; Add 1 to FPREG +4552 1CF5 CD D5 18 CALL MULLN2 ; Multiply by LN(2) +4553 1CF8 F1 POP AF ; Restore scaling factor +4554 1CF9 C1 POP BC ; Restore exponent +4555 1CFA D1 POP DE +4556 1CFB F5 PUSH AF ; Save scaling factor +4557 1CFC CD A0 17 CALL SUBCDE ; Subtract exponent from FPREG +4558 1CFF CD 12 1A CALL INVSGN ; Negate result +4559 1D02 21 10 1D LD HL,EXPTAB ; Coefficient table +4560 1D05 CD 40 1D CALL SMSER1 ; Sum the series +4561 1D08 11 00 00 LD DE,0 ; Zero LSBs +4562 1D0B C1 POP BC ; Scaling factor +4563 1D0C 4A LD C,D ; Zero MSB +4564 1D0D C3 DE 18 JP FPMULT ; Scale result to correct value +4565 1D10 +4566 1D10 08 EXPTAB: .BYTE 8 ; Table used by EXP +4567 1D11 40 2E 94 74 .BYTE 040H,02EH,094H,074H ; -1/7! (-1/5040) +4568 1D15 70 4F 2E 77 .BYTE 070H,04FH,02EH,077H ; 1/6! ( 1/720) +4569 1D19 6E 02 88 7A .BYTE 06EH,002H,088H,07AH ; -1/5! (-1/120) +4570 1D1D E6 A0 2A 7C .BYTE 0E6H,0A0H,02AH,07CH ; 1/4! ( 1/24) +4571 1D21 50 AA AA 7E .BYTE 050H,0AAH,0AAH,07EH ; -1/3! (-1/6) +4572 1D25 FF FF 7F 7F .BYTE 0FFH,0FFH,07FH,07FH ; 1/2! ( 1/2) +4573 1D29 00 00 80 81 .BYTE 000H,000H,080H,081H ; -1/1! (-1/1) +4574 1D2D 00 00 00 81 .BYTE 000H,000H,000H,081H ; 1/0! ( 1/1) +4575 1D31 +4576 1D31 CD 1A 1A SUMSER: CALL STAKFP ; Put FPREG on stack +4577 1D34 11 DC 18 LD DE,MULT ; Multiply by "X" +4578 1D37 D5 PUSH DE ; To be done after +4579 1D38 E5 PUSH HL ; Save address of table +4580 1D39 CD 35 1A CALL BCDEFP ; Move FPREG to BCDE +4581 1D3C CD DE 18 CALL FPMULT ; Square the value +4582 1D3F E1 POP HL ; Restore address of table +4583 1D40 CD 1A 1A SMSER1: CALL STAKFP ; Put value on stack +4584 1D43 7E LD A,(HL) ; Get number of coefficients +4585 1D44 23 INC HL ; Point to start of table +4586 1D45 CD 27 1A CALL PHLTFP ; Move coefficient to FPREG +4587 1D48 06 .BYTE 06H ; Skip "POP AF" +4588 1D49 F1 SUMLP: POP AF ; Restore count +4589 1D4A C1 POP BC ; Restore number +4590 1D4B D1 POP DE +4591 1D4C 3D DEC A ; Cont coefficients +4592 1D4D C8 RET Z ; All done +4593 1D4E D5 PUSH DE ; Save number +4594 1D4F C5 PUSH BC +4595 1D50 F5 PUSH AF ; Save count +4596 1D51 E5 PUSH HL ; Save address in table +4597 1D52 CD DE 18 CALL FPMULT ; Multiply FPREG by BCDE +4598 1D55 E1 POP HL ; Restore address in table +4599 1D56 CD 38 1A CALL LOADFP ; Number at HL to BCDE +4600 1D59 E5 PUSH HL ; Save address in table +4601 1D5A CD A3 17 CALL FPADD ; Add coefficient to FPREG +4602 1D5D E1 POP HL ; Restore address in table +4603 1D5E C3 49 1D JP SUMLP ; More coefficients +4604 1D61 +4605 1D61 CD E9 19 RND: CALL TSTSGN ; Test sign of FPREG +4606 1D64 21 C9 30 LD HL,SEED+2 ; Random number seed +4607 1D67 FA C2 1D JP M,RESEED ; Negative - Re-seed +4608 1D6A 21 EA 30 LD HL,LSTRND ; Last random number +4609 1D6D CD 27 1A CALL PHLTFP ; Move last RND to FPREG +4610 1D70 21 C9 30 LD HL,SEED+2 ; Random number seed +4611 1D73 C8 RET Z ; Return if RND(0) +4612 1D74 86 ADD A,(HL) ; Add (SEED)+2) +4613 1D75 E6 07 AND 00000111B ; 0 to 7 +4614 1D77 06 00 LD B,0 +4615 1D79 77 LD (HL),A ; Re-save seed +4616 1D7A 23 INC HL ; Move to coefficient table +4617 1D7B 87 ADD A,A ; 4 bytes +4618 1D7C 87 ADD A,A ; per entry +4619 1D7D 4F LD C,A ; BC = Offset into table +4620 1D7E 09 ADD HL,BC ; Point to coefficient +4621 1D7F CD 38 1A CALL LOADFP ; Coefficient to BCDE +4622 1D82 CD DE 18 CALL FPMULT ; ; Multiply FPREG by coefficient +4623 1D85 3A C8 30 LD A,(SEED+1) ; Get (SEED+1) +4624 1D88 3C INC A ; Add 1 +4625 1D89 E6 03 AND 00000011B ; 0 to 3 +4626 1D8B 06 00 LD B,0 +4627 1D8D FE 01 CP 1 ; Is it zero? +4628 1D8F 88 ADC A,B ; Yes - Make it 1 +4629 1D90 32 C8 30 LD (SEED+1),A ; Re-save seed +4630 1D93 21 C6 1D LD HL,RNDTAB-4 ; Addition table +4631 1D96 87 ADD A,A ; 4 bytes +4632 1D97 87 ADD A,A ; per entry +4633 1D98 4F LD C,A ; BC = Offset into table +4634 1D99 09 ADD HL,BC ; Point to value +4635 1D9A CD 94 17 CALL ADDPHL ; Add value to FPREG +4636 1D9D CD 35 1A RND1: CALL BCDEFP ; Move FPREG to BCDE +4637 1DA0 7B LD A,E ; Get LSB +4638 1DA1 59 LD E,C ; LSB = MSB +4639 1DA2 EE 4F XOR 01001111B ; Fiddle around +4640 1DA4 4F LD C,A ; New MSB +4641 1DA5 36 80 LD (HL),80H ; Set exponent +4642 1DA7 2B DEC HL ; Point to MSB +4643 1DA8 46 LD B,(HL) ; Get MSB +4644 1DA9 36 80 LD (HL),80H ; Make value -0.5 +4645 1DAB 21 C7 30 LD HL,SEED ; Random number seed +4646 1DAE 34 INC (HL) ; Count seed +4647 1DAF 7E LD A,(HL) ; Get seed +4648 1DB0 D6 AB SUB 171 ; Do it modulo 171 +4649 1DB2 C2 B9 1D JP NZ,RND2 ; Non-zero - Ok +4650 1DB5 77 LD (HL),A ; Zero seed +4651 1DB6 0C INC C ; Fillde about +4652 1DB7 15 DEC D ; with the +4653 1DB8 1C INC E ; number +4654 1DB9 CD F4 17 RND2: CALL BNORM ; Normalise number +4655 1DBC 21 EA 30 LD HL,LSTRND ; Save random number +4656 1DBF C3 41 1A JP FPTHL ; Move FPREG to last and return +4657 1DC2 +4658 1DC2 77 RESEED: LD (HL),A ; Re-seed random numbers +4659 1DC3 2B DEC HL +4660 1DC4 77 LD (HL),A +4661 1DC5 2B DEC HL +4662 1DC6 77 LD (HL),A +4663 1DC7 C3 9D 1D JP RND1 ; Return RND seed +4664 1DCA +4665 1DCA 68 B1 46 68 RNDTAB: .BYTE 068H,0B1H,046H,068H ; Table used by RND +4666 1DCE 99 E9 92 69 .BYTE 099H,0E9H,092H,069H +4667 1DD2 10 D1 75 68 .BYTE 010H,0D1H,075H,068H +4668 1DD6 +4669 1DD6 21 20 1E COS: LD HL,HALFPI ; Point to PI/2 +4670 1DD9 CD 94 17 CALL ADDPHL ; Add it to PPREG +4671 1DDC CD 1A 1A SIN: CALL STAKFP ; Put angle on stack +4672 1DDF 01 49 83 LD BC,8349H ; BCDE = 2 PI +4673 1DE2 11 DB 0F LD DE,0FDBH +4674 1DE5 CD 2A 1A CALL FPBCDE ; Move 2 PI to FPREG +4675 1DE8 C1 POP BC ; Restore angle +4676 1DE9 D1 POP DE +4677 1DEA CD 3F 19 CALL DVBCDE ; Divide angle by 2 PI +4678 1DED CD 1A 1A CALL STAKFP ; Put it on stack +4679 1DF0 CD BC 1A CALL INT ; Get INT of result +4680 1DF3 C1 POP BC ; Restore number +4681 1DF4 D1 POP DE +4682 1DF5 CD A0 17 CALL SUBCDE ; Make it 0 <= value < 1 +4683 1DF8 21 24 1E LD HL,QUARTR ; Point to 0.25 +4684 1DFB CD 9A 17 CALL SUBPHL ; Subtract value from 0.25 +4685 1DFE CD E9 19 CALL TSTSGN ; Test sign of value +4686 1E01 37 SCF ; Flag positive +4687 1E02 F2 0C 1E JP P,SIN1 ; Positive - Ok +4688 1E05 CD 91 17 CALL ROUND ; Add 0.5 to value +4689 1E08 CD E9 19 CALL TSTSGN ; Test sign of value +4690 1E0B B7 OR A ; Flag negative +4691 1E0C F5 SIN1: PUSH AF ; Save sign +4692 1E0D F4 12 1A CALL P,INVSGN ; Negate value if positive +4693 1E10 21 24 1E LD HL,QUARTR ; Point to 0.25 +4694 1E13 CD 94 17 CALL ADDPHL ; Add 0.25 to value +4695 1E16 F1 POP AF ; Restore sign +4696 1E17 D4 12 1A CALL NC,INVSGN ; Negative - Make positive +4697 1E1A 21 28 1E LD HL,SINTAB ; Coefficient table +4698 1E1D C3 31 1D JP SUMSER ; Evaluate sum of series +4699 1E20 +4700 1E20 DB 0F 49 81 HALFPI: .BYTE 0DBH,00FH,049H,081H ; 1.5708 (PI/2) +4701 1E24 +4702 1E24 00 00 00 7F QUARTR: .BYTE 000H,000H,000H,07FH ; 0.25 +4703 1E28 +4704 1E28 05 SINTAB: .BYTE 5 ; Table used by SIN +4705 1E29 BA D7 1E 86 .BYTE 0BAH,0D7H,01EH,086H ; 39.711 +4706 1E2D 64 26 99 87 .BYTE 064H,026H,099H,087H ;-76.575 +4707 1E31 58 34 23 87 .BYTE 058H,034H,023H,087H ; 81.602 +4708 1E35 E0 5D A5 86 .BYTE 0E0H,05DH,0A5H,086H ;-41.342 +4709 1E39 DA 0F 49 83 .BYTE 0DAH,00FH,049H,083H ; 6.2832 +4710 1E3D +4711 1E3D CD 1A 1A TAN: CALL STAKFP ; Put angle on stack +4712 1E40 CD DC 1D CALL SIN ; Get SIN of angle +4713 1E43 C1 POP BC ; Restore angle +4714 1E44 E1 POP HL +4715 1E45 CD 1A 1A CALL STAKFP ; Save SIN of angle +4716 1E48 EB EX DE,HL ; BCDE = Angle +4717 1E49 CD 2A 1A CALL FPBCDE ; Angle to FPREG +4718 1E4C CD D6 1D CALL COS ; Get COS of angle +4719 1E4F C3 3D 19 JP DIV ; TAN = SIN / COS +4720 1E52 +4721 1E52 CD E9 19 ATN: CALL TSTSGN ; Test sign of value +4722 1E55 FC 7D 1C CALL M,NEGAFT ; Negate result after if -ve +4723 1E58 FC 12 1A CALL M,INVSGN ; Negate value if -ve +4724 1E5B 3A 97 31 LD A,(FPEXP) ; Get exponent +4725 1E5E FE 81 CP 81H ; Number less than 1? +4726 1E60 DA 6F 1E JP C,ATN1 ; Yes - Get arc tangnt +4727 1E63 01 00 81 LD BC,8100H ; BCDE = 1 +4728 1E66 51 LD D,C +4729 1E67 59 LD E,C +4730 1E68 CD 3F 19 CALL DVBCDE ; Get reciprocal of number +4731 1E6B 21 9A 17 LD HL,SUBPHL ; Sub angle from PI/2 +4732 1E6E E5 PUSH HL ; Save for angle > 1 +4733 1E6F 21 79 1E ATN1: LD HL,ATNTAB ; Coefficient table +4734 1E72 CD 31 1D CALL SUMSER ; Evaluate sum of series +4735 1E75 21 20 1E LD HL,HALFPI ; PI/2 - angle in case > 1 +4736 1E78 C9 RET ; Number > 1 - Sub from PI/2 +4737 1E79 +4738 1E79 09 ATNTAB: .BYTE 9 ; Table used by ATN +4739 1E7A 4A D7 3B 78 .BYTE 04AH,0D7H,03BH,078H ; 1/17 +4740 1E7E 02 6E 84 7B .BYTE 002H,06EH,084H,07BH ;-1/15 +4741 1E82 FE C1 2F 7C .BYTE 0FEH,0C1H,02FH,07CH ; 1/13 +4742 1E86 74 31 9A 7D .BYTE 074H,031H,09AH,07DH ;-1/11 +4743 1E8A 84 3D 5A 7D .BYTE 084H,03DH,05AH,07DH ; 1/9 +4744 1E8E C8 7F 91 7E .BYTE 0C8H,07FH,091H,07EH ;-1/7 +4745 1E92 E4 BB 4C 7E .BYTE 0E4H,0BBH,04CH,07EH ; 1/5 +4746 1E96 6C AA AA 7F .BYTE 06CH,0AAH,0AAH,07FH ;-1/3 +4747 1E9A 00 00 00 81 .BYTE 000H,000H,000H,081H ; 1/1 +4748 1E9E +4749 1E9E +4750 1E9E C9 ARET: RET ; A RETurn instruction +4751 1E9F +4752 1E9F D7 GETINP: RST 10H ;input a character +4753 1EA0 C9 RET +4754 1EA1 +4755 1EA1 CLS: +4756 1EA1 3E 0C LD A,CS ; ASCII Clear screen +4757 1EA3 C3 DB 1F JP MONOUT ; Output character +4758 1EA6 +4759 1EA6 CD 68 17 WIDTH: CALL GETINT ; Get integer 0-255 +4760 1EA9 7B LD A,E ; Width to A +4761 1EAA 32 F2 30 LD (LWIDTH),A ; Set width +4762 1EAD C9 RET +4763 1EAE +4764 1EAE CD 07 10 LINES: CALL GETNUM ; Get a number +4765 1EB1 CD 4C 0C CALL DEINT ; Get integer -32768 to 32767 +4766 1EB4 ED 53 F6 30 LD (LINESC),DE ; Set lines counter +4767 1EB8 ED 53 F8 30 LD (LINESN),DE ; Set lines number +4768 1EBC C9 RET +4769 1EBD +4770 1EBD CD 4C 0C DEEK: CALL DEINT ; Get integer -32768 to 32767 +4771 1EC0 D5 PUSH DE ; Save number +4772 1EC1 E1 POP HL ; Number to HL +4773 1EC2 46 LD B,(HL) ; Get LSB of contents +4774 1EC3 23 INC HL +4775 1EC4 7E LD A,(HL) ; Get MSB of contents +4776 1EC5 C3 C2 13 JP ABPASS ; Return integer AB +4777 1EC8 +4778 1EC8 CD 07 10 DOKE: CALL GETNUM ; Get a number +4779 1ECB CD 4C 0C CALL DEINT ; Get integer -32768 to 32767 +4780 1ECE D5 PUSH DE ; Save address +4781 1ECF CD 10 0A CALL CHKSYN ; Make sure ',' follows +4782 1ED2 2C .BYTE ',' +4783 1ED3 CD 07 10 CALL GETNUM ; Get a number +4784 1ED6 CD 4C 0C CALL DEINT ; Get integer -32768 to 32767 +4785 1ED9 E3 EX (SP),HL ; Save value,get address +4786 1EDA 73 LD (HL),E ; Save LSB of value +4787 1EDB 23 INC HL +4788 1EDC 72 LD (HL),D ; Save MSB of value +4789 1EDD E1 POP HL ; Restore code string address +4790 1EDE C9 RET +4791 1EDF +4792 1EDF +4793 1EDF ; HEX$(nn) Convert 16 bit number to Hexadecimal string +4794 1EDF +4795 1EDF CD 0A 10 HEX: CALL TSTNUM ; Verify it's a number +4796 1EE2 CD 4C 0C CALL DEINT ; Get integer -32768 to 32767 +4797 1EE5 C5 PUSH BC ; Save contents of BC +4798 1EE6 21 99 31 LD HL,PBUFF +4799 1EE9 7A LD A,D ; Get high order into A +4800 1EEA FE 00 CP $0 +4801 1EEC 28 0C JR Z,HEX2 ; Skip output if both high digits are zero +4802 1EEE CD 17 1F CALL BYT2ASC ; Convert D to ASCII +4803 1EF1 78 LD A,B +4804 1EF2 FE 30 CP '0' +4805 1EF4 28 02 JR Z,HEX1 ; Don't store high digit if zero +4806 1EF6 70 LD (HL),B ; Store it to PBUFF +4807 1EF7 23 INC HL ; Next location +4808 1EF8 71 HEX1: LD (HL),C ; Store C to PBUFF+1 +4809 1EF9 23 INC HL ; Next location +4810 1EFA 7B HEX2: LD A,E ; Get lower byte +4811 1EFB CD 17 1F CALL BYT2ASC ; Convert E to ASCII +4812 1EFE 7A LD A,D +4813 1EFF FE 00 CP $0 +4814 1F01 20 05 JR NZ,HEX3 ; If upper byte was not zero then always print lower byte +4815 1F03 78 LD A,B +4816 1F04 FE 30 CP '0' ; If high digit of lower byte is zero then don't print +4817 1F06 28 02 JR Z,HEX4 +4818 1F08 70 HEX3: LD (HL),B ; to PBUFF+2 +4819 1F09 23 INC HL ; Next location +4820 1F0A 71 HEX4: LD (HL),C ; to PBUFF+3 +4821 1F0B 23 INC HL ; PBUFF+4 to zero +4822 1F0C AF XOR A ; Terminating character +4823 1F0D 77 LD (HL),A ; Store zero to terminate +4824 1F0E 23 INC HL ; Make sure PBUFF is terminated +4825 1F0F 77 LD (HL),A ; Store the double zero there +4826 1F10 C1 POP BC ; Get BC back +4827 1F11 21 99 31 LD HL,PBUFF ; Reset to start of PBUFF +4828 1F14 C3 70 14 JP STR1 ; Convert the PBUFF to a string and return it +4829 1F17 +4830 1F17 47 BYT2ASC LD B,A ; Save original value +4831 1F18 E6 0F AND $0F ; Strip off upper nybble +4832 1F1A FE 0A CP $0A ; 0-9? +4833 1F1C 38 02 JR C,ADD30 ; If A-F, add 7 more +4834 1F1E C6 07 ADD A,$07 ; Bring value up to ASCII A-F +4835 1F20 C6 30 ADD30 ADD A,$30 ; And make ASCII +4836 1F22 4F LD C,A ; Save converted char to C +4837 1F23 78 LD A,B ; Retrieve original value +4838 1F24 0F RRCA ; and Rotate it right +4839 1F25 0F RRCA +4840 1F26 0F RRCA +4841 1F27 0F RRCA +4842 1F28 E6 0F AND $0F ; Mask off upper nybble +4843 1F2A FE 0A CP $0A ; 0-9? < A hex? +4844 1F2C 38 02 JR C,ADD301 ; Skip Add 7 +4845 1F2E C6 07 ADD A,$07 ; Bring it up to ASCII A-F +4846 1F30 C6 30 ADD301 ADD A,$30 ; And make it full ASCII +4847 1F32 47 LD B,A ; Store high order byte +4848 1F33 C9 RET +4849 1F34 +4850 1F34 ; Convert "&Hnnnn" to FPREG +4851 1F34 ; Gets a character from (HL) checks for Hexadecimal ASCII numbers "&Hnnnn" +4852 1F34 ; Char is in A, NC if char is ;<=>?@ A-z, CY is set if 0-9 +4853 1F34 EB HEXTFP EX DE,HL ; Move code string pointer to DE +4854 1F35 21 00 00 LD HL,$0000 ; Zero out the value +4855 1F38 CD 4D 1F CALL GETHEX ; Check the number for valid hex +4856 1F3B DA 6D 1F JP C,HXERR ; First value wasn't hex, HX error +4857 1F3E 18 05 JR HEXLP1 ; Convert first character +4858 1F40 CD 4D 1F HEXLP CALL GETHEX ; Get second and addtional characters +4859 1F43 38 1F JR C,HEXIT ; Exit if not a hex character +4860 1F45 29 HEXLP1 ADD HL,HL ; Rotate 4 bits to the left +4861 1F46 29 ADD HL,HL +4862 1F47 29 ADD HL,HL +4863 1F48 29 ADD HL,HL +4864 1F49 B5 OR L ; Add in D0-D3 into L +4865 1F4A 6F LD L,A ; Save new value +4866 1F4B 18 F3 JR HEXLP ; And continue until all hex characters are in +4867 1F4D +4868 1F4D 13 GETHEX INC DE ; Next location +4869 1F4E 1A LD A,(DE) ; Load character at pointer +4870 1F4F FE 20 CP ' ' +4871 1F51 CA 4D 1F JP Z,GETHEX ; Skip spaces +4872 1F54 D6 30 SUB $30 ; Get absolute value +4873 1F56 D8 RET C ; < "0", error +4874 1F57 FE 0A CP $0A +4875 1F59 38 05 JR C,NOSUB7 ; Is already in the range 0-9 +4876 1F5B D6 07 SUB $07 ; Reduce to A-F +4877 1F5D FE 0A CP $0A ; Value should be $0A-$0F at this point +4878 1F5F D8 RET C ; CY set if was : ; < = > ? @ +4879 1F60 FE 10 NOSUB7 CP $10 ; > Greater than "F"? +4880 1F62 3F CCF +4881 1F63 C9 RET ; CY set if it wasn't valid hex +4882 1F64 +4883 1F64 EB HEXIT EX DE,HL ; Value into DE, Code string into HL +4884 1F65 7A LD A,D ; Load DE into AC +4885 1F66 4B LD C,E ; For prep to +4886 1F67 E5 PUSH HL +4887 1F68 CD C1 13 CALL ACPASS ; ACPASS to set AC as integer into FPREG +4888 1F6B E1 POP HL +4889 1F6C C9 RET +4890 1F6D +4891 1F6D 1E 26 HXERR: LD E,HX ; ?HEX Error +4892 1F6F C3 56 07 JP ERROR +4893 1F72 +4894 1F72 ; BIN$(NN) Convert integer to a 1-16 char binary string +4895 1F72 CD 0A 10 BIN: CALL TSTNUM ; Verify it's a number +4896 1F75 CD 4C 0C CALL DEINT ; Get integer -32768 to 32767 +4897 1F78 C5 BIN2: PUSH BC ; Save contents of BC +4898 1F79 21 99 31 LD HL,PBUFF +4899 1F7C 06 11 LD B,17 ; One higher than max char count +4900 1F7E ZEROSUP: ; Suppress leading zeros +4901 1F7E 05 DEC B ; Max 16 chars +4902 1F7F 78 LD A,B +4903 1F80 FE 01 CP $01 +4904 1F82 28 08 JR Z,BITOUT ; Always output at least one character +4905 1F84 CB 13 RL E +4906 1F86 CB 12 RL D +4907 1F88 30 F4 JR NC,ZEROSUP +4908 1F8A 18 04 JR BITOUT2 +4909 1F8C BITOUT: +4910 1F8C CB 13 RL E +4911 1F8E CB 12 RL D ; Top bit now in carry +4912 1F90 BITOUT2: +4913 1F90 3E 30 LD A,'0' ; Char for '0' +4914 1F92 CE 00 ADC A,0 ; If carry set then '0' --> '1' +4915 1F94 77 LD (HL),A +4916 1F95 23 INC HL +4917 1F96 05 DEC B +4918 1F97 20 F3 JR NZ,BITOUT +4919 1F99 AF XOR A ; Terminating character +4920 1F9A 77 LD (HL),A ; Store zero to terminate +4921 1F9B 23 INC HL ; Make sure PBUFF is terminated +4922 1F9C 77 LD (HL),A ; Store the double zero there +4923 1F9D C1 POP BC +4924 1F9E 21 99 31 LD HL,PBUFF +4925 1FA1 C3 70 14 JP STR1 +4926 1FA4 +4927 1FA4 ; Convert "&Bnnnn" to FPREG +4928 1FA4 ; Gets a character from (HL) checks for Binary ASCII numbers "&Bnnnn" +4929 1FA4 EB BINTFP: EX DE,HL ; Move code string pointer to DE +4930 1FA5 21 00 00 LD HL,$0000 ; Zero out the value +4931 1FA8 CD C1 1F CALL CHKBIN ; Check the number for valid bin +4932 1FAB DA CF 1F JP C,BINERR ; First value wasn't bin, HX error +4933 1FAE D6 30 BINIT: SUB '0' +4934 1FB0 29 ADD HL,HL ; Rotate HL left +4935 1FB1 B5 OR L +4936 1FB2 6F LD L,A +4937 1FB3 CD C1 1F CALL CHKBIN ; Get second and addtional characters +4938 1FB6 30 F6 JR NC,BINIT ; Process if a bin character +4939 1FB8 EB EX DE,HL ; Value into DE, Code string into HL +4940 1FB9 7A LD A,D ; Load DE into AC +4941 1FBA 4B LD C,E ; For prep to +4942 1FBB E5 PUSH HL +4943 1FBC CD C1 13 CALL ACPASS ; ACPASS to set AC as integer into FPREG +4944 1FBF E1 POP HL +4945 1FC0 C9 RET +4946 1FC1 +4947 1FC1 ; Char is in A, NC if char is 0 or 1 +4948 1FC1 13 CHKBIN: INC DE +4949 1FC2 1A LD A,(DE) +4950 1FC3 FE 20 CP ' ' +4951 1FC5 CA C1 1F JP Z,CHKBIN ; Skip spaces +4952 1FC8 FE 30 CP '0' ; Set C if < '0' +4953 1FCA D8 RET C +4954 1FCB FE 32 CP '2' +4955 1FCD 3F CCF ; Set C if > '1' +4956 1FCE C9 RET +4957 1FCF +4958 1FCF 1E 28 BINERR: LD E,BN ; ?BIN Error +4959 1FD1 C3 56 07 JP ERROR +4960 1FD4 +4961 1FD4 +4962 1FD4 JJUMP1: +4963 1FD4 DD 21 FF FF LD IX,-1 ; Flag cold start +4964 1FD8 C3 A6 03 JP CSTART ; Go and initialise +4965 1FDB +4966 1FDB MONOUT: +4967 1FDB C3 08 00 JP $0008 ; output a char +4968 1FDE +4969 1FDE +4970 1FDE MONITR: +4971 1FDE C3 00 00 JP $0000 ; Restart (Normally Monitor Start) +4972 1FE1 +4973 1FE1 +4974 1FE1 3E 00 INITST: LD A,0 ; Clear break flag +4975 1FE3 32 FD 30 LD (BRKFLG),A +4976 1FE6 C3 AD 03 JP INIT +4977 1FE9 +4978 1FE9 ED 45 ARETN: RETN ; Return from NMI +4979 1FEB +4980 1FEB +4981 1FEB F5 TSTBIT: PUSH AF ; Save bit mask +4982 1FEC A0 AND B ; Get common bits +4983 1FED C1 POP BC ; Restore bit mask +4984 1FEE B8 CP B ; Same bit set? +4985 1FEF 3E 00 LD A,0 ; Return 0 in A +4986 1FF1 C9 RET +4987 1FF2 +4988 1FF2 CD 1B 0A OUTNCR: CALL OUTC ; Output character in A +4989 1FF5 C3 42 0E JP PRNTCRLF ; Output CRLF +4990 1FF8 +4991 1FF8 .end +4992 1FF8 +tasm: Number of errors = 0 diff --git a/Z80 CPM and bootloader (basmon)/source/BASMON.OBJ b/Z80 CPM and bootloader (basmon)/source/BASMON.OBJ new file mode 100644 index 0000000..02f308a --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/BASMON.OBJ @@ -0,0 +1,52 @@ +:0430040000000000C8 +:180000000D0A426F6F742043502F4D3F000D0A4C6F6164696E67204397 +:18001800502F4D2E2E2E0D0A0043502F4D20426F6F7420524F4D203240 +:180030002E3020627920472E20536561726C650D0A0D0A4243206F729A +:18004800204257202D20524F4D20424153494320436F6C642F5761720F +:180060006D0D0A5820202020202020202D20426F6F742043502F4D207C +:18007800286C6F61642024443030302D2446464646290D0A3A6E6E6E63 +:180090006E2E2E2E202D204C6F616420496E74656C2D48657820666916 +:1800A8006C65207265636F72640D0A476E6E6E6E202020202D20527526 +:1800C0006E206C6F63206E6E6E6E0D0A000D0A436F6C64206F7220773C +:1800D80061726D3F0D0A00436865636B73756D206572726F720D0A00E6 +:1800F0000C5072657373205B53504143455D20746F2061637469766100 +:18010800746520636F6E736F6C650D0A00436F6D706C6574650D0A008C +:180120000000000020427974657320667265650D0A00005A38302042A3 +:18013800415349432056657220342E37620D0A436F7079726967687457 +:18015000202843292031393738206279204D6963726F736F66740D0A02 +:1801680000004D656D6F727920746F7000000000000000B330000000B0 +:1801800000000000000000000000000000000000000000000000000166 +:18019800310000000000000000000000000000000000000000C54E44C7 +:1801B000C64F52CE455854C4415441C94E505554C4494DD2454144CCA5 +:1801C8004554C74F544FD2554EC946D24553544F5245C74F535542D2D3 +:1801E000455455524ED2454DD3544F50CF5554CF4ECE554C4CD741499E +:1801F80054C44546D04F4B45C44F4B45D3435245454ECC494E4553C3FC +:180210004C53D749445448CD4F4E49544F52D34554D245534554D052FE +:18022800494E54C34F4E54CC495354C34C454152C34C4F4144C3534142 +:180240005645CE4557D4414228D44FC64ED3504328D448454ECE4F543D +:18025800D3544550ABADAAAFDEC14E44CF52BEBDBCD3474EC94E54C104 +:180270004253D55352C65245C94E50D04F53D35152D24E44CC4F47C530 +:180288005850C34F53D3494ED4414EC1544ED045454BC445454BD04FC4 +:1802A000494E54CC454ED3545224D6414CC15343C3485224C84558249B +:1802B800C2494E24CC45465424D24947485424CD4944248000000000C2 +:1802D0000000B50300000000000000000000000000000000000000005E +:1802E80000000000000000000000000000000000000000000000980462 +:1803000000009904043107310000000000000000000000000000790062 +:18031800007900007C00007C00007F000050000046E8034E46534E52D5 +:18033000474F4446434F564F4D554C425344442F304944544D4F534CD8 +:18034800535354434E55464D4F4858424E000000354ACA99391C769866 +:180360002295B3980ADD479853D199990A1A9F9865BCCD98D6773E9863 +:1803780052C74F8001FF1C000014001400000000000D32FEFFAA31200A +:180390004572726F720020696E20004F6B0D0A0000427265616B003E40 +:1803A8000101010101013E3EA6F6212C3E013AB43E88A9293F526564B3 +:1803C0006F2066726F6D2073746172740D0A003B3EF62C3F4578747200 +:1803D800612069676E6F7265640D0A00F628292CF600002CF629113E8A +:1803F000162829B4A70E112C29292C2C2C210000008103AA561980F1E3 +:1804080022768045AA388221D2FE00000080A08601102700E8030064FD +:1804200000000A000001000008402E9474704F2E776E02887AE6A02AB5 +:180438007C50AAAA7EFFFF7F7F00008081000000810668B1466899E941 +:18045000926910D17568DB0F49810000007F05BAD71E8664269987586C +:18046800342387E05DA586DA0F4983094AD73B78026E847BFEC12F7CCB +:1804800074319A7D843D5A7DC87F917EE4BB4C7E6CAAAA7F0000008191 +:010498002C37 +:00000001FF diff --git a/Z80 CPM and bootloader (basmon)/source/CBIOS128.LST b/Z80 CPM and bootloader (basmon)/source/CBIOS128.LST new file mode 100644 index 0000000..b3c6cd7 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/CBIOS128.LST @@ -0,0 +1,947 @@ +0001 0000 ;================================================================================== +0002 0000 ; Contents of this file are copyright Grant Searle +0003 0000 ; Blocking/unblocking routines are the published version by Digital Research +0004 0000 ; (bugfixed, as found on the web) +0005 0000 ; +0006 0000 ; You have permission to use this for NON COMMERCIAL USE ONLY +0007 0000 ; If you wish to use it elsewhere, please include an acknowledgement to myself. +0008 0000 ; +0009 0000 ; http://searle.hostei.com/grant/index.html +0010 0000 ; +0011 0000 ; eMail: home.micros01@btinternet.com +0012 0000 ; +0013 0000 ; If the above don't work, please perform an Internet search to see if I have +0014 0000 ; updated the web page hosting service. +0015 0000 ; +0016 0000 ;================================================================================== +0017 0000 +0018 0000 ccp .EQU 0D000h ; Base of CCP. +0019 0000 bdos .EQU ccp + 0806h ; Base of BDOS. +0020 0000 bios .EQU ccp + 1600h ; Base of BIOS. +0021 0000 +0022 0000 ; Set CP/M low memory datA, vector and buffer addresses. +0023 0000 +0024 0000 iobyte .EQU 03h ; Intel standard I/O definition byte. +0025 0000 userdrv .EQU 04h ; Current user number and drive. +0026 0000 tpabuf .EQU 80h ; Default I/O buffer and command line storage. +0027 0000 +0028 0000 +0029 0000 SD_DATA .EQU 088H +0030 0000 SD_CONTROL .EQU 089H +0031 0000 SD_STATUS .EQU 089H +0032 0000 SD_LBA0 .EQU 08AH +0033 0000 SD_LBA1 .EQU 08BH +0034 0000 SD_LBA2 .EQU 08CH +0035 0000 +0036 0000 RTS_HIGH .EQU 0D5H +0037 0000 RTS_LOW .EQU 095H +0038 0000 +0039 0000 ACIA0_D .EQU $81 +0040 0000 ACIA0_C .EQU $80 +0041 0000 ACIA1_D .EQU $83 +0042 0000 ACIA1_C .EQU $82 +0043 0000 +0044 0000 nmi .EQU 66H +0045 0000 +0046 0000 blksiz .equ 4096 ;CP/M allocation size +0047 0000 hstsiz .equ 512 ;host disk sector size +0048 0000 hstspt .equ 32 ;host disk sectors/trk +0049 0000 hstblk .equ hstsiz/128 ;CP/M sects/host buff +0050 0000 cpmspt .equ hstblk * hstspt ;CP/M sectors/track +0051 0000 secmsk .equ hstblk-1 ;sector mask +0052 0000 ;compute sector mask +0053 0000 ;secshf .equ 2 ;log2(hstblk) +0054 0000 +0055 0000 wrall .equ 0 ;write to allocated +0056 0000 wrdir .equ 1 ;write to directory +0057 0000 wrual .equ 2 ;write to unallocated +0058 0000 +0059 0000 LF .EQU 0AH ;line feed +0060 0000 FF .EQU 0CH ;form feed +0061 0000 CR .EQU 0DH ;carriage RETurn +0062 0000 +0063 0000 ;================================================================================================ +0064 0000 +0065 E600 .ORG bios ; BIOS origin. +0066 E600 +0067 E600 ;================================================================================================ +0068 E600 ; BIOS jump table. +0069 E600 ;================================================================================================ +0070 E600 C3 51 E7 JP boot ; 0 Initialize. +0071 E603 C3 B5 E7 wboote: JP wboot ; 1 Warm boot. +0072 E606 C3 1D E8 JP const ; 2 Console status. +0073 E609 C3 58 E8 JP conin ; 3 Console input. +0074 E60C C3 96 E8 JP conout ; 4 Console OUTput. +0075 E60F C3 7E E8 JP list ; 5 List OUTput. +0076 E612 C3 8A E8 JP punch ; 6 punch OUTput. +0077 E615 C3 4C E8 JP reader ; 7 Reader input. +0078 E618 C3 EF E8 JP home ; 8 Home disk. +0079 E61B C3 C7 E8 JP seldsk ; 9 Select disk. +0080 E61E C3 FB E8 JP settrk ; 10 Select track. +0081 E621 C3 00 E9 JP setsec ; 11 Select sector. +0082 E624 C3 05 E9 JP setdma ; 12 Set DMA ADDress. +0083 E627 C3 0D E9 JP read ; 13 Read 128 bytes. +0084 E62A C3 21 E9 JP write ; 14 Write 128 bytes. +0085 E62D C3 C4 E8 JP listst ; 15 List status. +0086 E630 C3 0A E9 JP sectran ; 16 Sector translate. +0087 E633 +0088 E633 ;================================================================================================ +0089 E633 ; Disk parameter headers for disk 0 to 15 +0090 E633 ;================================================================================================ +0091 E633 dpbase: +0092 E633 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb0,0000h,alv00 +0092 E639 00000DEB33E700008DEB +0093 E643 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv01 +0093 E649 00000DEB42E700008EEC +0094 E653 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv02 +0094 E659 00000DEB42E700008FED +0095 E663 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv03 +0095 E669 00000DEB42E7000090EE +0096 E673 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv04 +0096 E679 00000DEB42E7000091EF +0097 E683 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv05 +0097 E689 00000DEB42E7000092F0 +0098 E693 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv06 +0098 E699 00000DEB42E7000093F1 +0099 E6A3 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv07 +0099 E6A9 00000DEB42E7000094F2 +0100 E6B3 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv08 +0100 E6B9 00000DEB42E7000095F3 +0101 E6C3 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv09 +0101 E6C9 00000DEB42E7000096F4 +0102 E6D3 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv10 +0102 E6D9 00000DEB42E7000097F5 +0103 E6E3 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv11 +0103 E6E9 00000DEB42E7000098F6 +0104 E6F3 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv12 +0104 E6F9 00000DEB42E7000099F7 +0105 E703 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv13 +0105 E709 00000DEB42E700009AF8 +0106 E713 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv14 +0106 E719 00000DEB42E700009BF9 +0107 E723 000000000000 .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv15 +0107 E729 00000DEB42E700009CFA +0108 E733 +0109 E733 ; First drive has a reserved track for CP/M +0110 E733 dpb0: +0111 E733 80 00 .DW 128 ;SPT - sectors per track +0112 E735 05 .DB 5 ;BSH - block shift factor +0113 E736 1F .DB 31 ;BLM - block mask +0114 E737 01 .DB 1 ;EXM - Extent mask +0115 E738 FB 07 .DW 2043 ; (2047-4) DSM - Storage size (blocks - 1) +0116 E73A FF 01 .DW 511 ;DRM - Number of directory entries - 1 +0117 E73C F0 .DB 240 ;AL0 - 1 bit set per directory block +0118 E73D 00 .DB 0 ;AL1 - " +0119 E73E 00 00 .DW 0 ;CKS - DIR check vector size (DRM+1)/4 (0=fixed disk) +0120 E740 01 00 .DW 1 ;OFF - Reserved tracks +0121 E742 +0122 E742 dpb: +0123 E742 80 00 .DW 128 ;SPT - sectors per track +0124 E744 05 .DB 5 ;BSH - block shift factor +0125 E745 1F .DB 31 ;BLM - block mask +0126 E746 01 .DB 1 ;EXM - Extent mask +0127 E747 FF 07 .DW 2047 ;DSM - Storage size (blocks - 1) +0128 E749 FF 01 .DW 511 ;DRM - Number of directory entries - 1 +0129 E74B F0 .DB 240 ;AL0 - 1 bit set per directory block +0130 E74C 00 .DB 0 ;AL1 - " +0131 E74D 00 00 .DW 0 ;CKS - DIR check vector size (DRM+1)/4 (0=fixed disk) +0132 E74F 00 00 .DW 0 ;OFF - Reserved tracks +0133 E751 +0134 E751 ;================================================================================================ +0135 E751 ; Cold boot +0136 E751 ;================================================================================================ +0137 E751 +0138 E751 boot: +0139 E751 F3 DI ; Disable interrupts. +0140 E752 31 C1 FB LD SP,biosstack ; Set default stack. +0141 E755 +0142 E755 ; Turn off ROM +0143 E755 +0144 E755 3E 01 LD A,$01 +0145 E757 D3 38 OUT ($38),A +0146 E759 +0147 E759 3E 95 LD A,RTS_LOW +0148 E75B D3 80 OUT (ACIA0_C),A ; Initialise ACIA0 +0149 E75D D3 82 OUT (ACIA1_C),A ; Initialise ACIA1 +0150 E75F +0151 E75F CD F9 EA CALL printInline +0152 E762 0C .DB FF +0153 E763 43502F4D2042 .TEXT "CP/M BIOS 2.0 by G. Searle 2013" +0153 E769 494F5320322E3020627920472E20536561726C652032303133 +0154 E782 0D 0A .DB CR,LF +0155 E784 0D 0A .DB CR,LF +0156 E786 43502F4D2032 .TEXT "CP/M 2.2 " +0156 E78C 2E3220 +0157 E78F 28 63 29 .TEXT "(c)" +0158 E792 203139373920 .TEXT " 1979 by Digital Research" +0158 E798 6279204469676974616C205265736561726368 +0159 E7AB 0D 0A 00 .DB CR,LF,0 +0160 E7AE +0161 E7AE ; CALL sdPreamble?? +0162 E7AE +0163 E7AE AF XOR a ; Clear I/O & drive bytes. +0164 E7AF 32 04 00 LD (userdrv),A +0165 E7B2 C3 F5 E7 JP gocpm +0166 E7B5 +0167 E7B5 ;================================================================================================ +0168 E7B5 ; Warm boot +0169 E7B5 ;================================================================================================ +0170 E7B5 +0171 E7B5 wboot: +0172 E7B5 F3 DI ; Disable interrupts. +0173 E7B6 31 C1 FB LD SP,biosstack ; Set default stack. +0174 E7B9 +0175 E7B9 06 0B LD B,11 ; Number of sectors to reload +0176 E7BB +0177 E7BB 3E 00 LD A,0 +0178 E7BD 32 C9 FB LD (hstsec),A +0179 E7C0 D3 8C OUT (SD_LBA2),A +0180 E7C2 D3 8B OUT (SD_LBA1),A +0181 E7C4 +0182 E7C4 21 00 D0 LD HL,ccp +0183 E7C7 +0184 E7C7 wbRdAllSecs: +0185 E7C7 +0186 E7C7 DB 89 wBrdWait1: IN A,(SD_STATUS) +0187 E7C9 FE 80 CP 128 +0188 E7CB 20 FA JR NZ,wBrdWait1 +0189 E7CD +0190 E7CD 3A C9 FB LD A,(hstsec) +0191 E7D0 D3 8A OUT (SD_LBA0),A +0192 E7D2 +0193 E7D2 3E 00 LD A,$00 ; 00 = Read block +0194 E7D4 D3 89 OUT (SD_CONTROL),A +0195 E7D6 C5 PUSH BC +0196 E7D7 +0197 E7D7 0E 04 LD c,4 +0198 E7D9 wBrd4secs: +0199 E7D9 06 80 LD b,128 +0200 E7DB wBrdByte: +0201 E7DB +0202 E7DB DB 89 wBrdWait2: IN A,(SD_STATUS) +0203 E7DD FE E0 CP 224 ; Read byte waiting +0204 E7DF 20 FA JR NZ,wBrdWait2 +0205 E7E1 +0206 E7E1 DB 88 IN A,(SD_DATA) +0207 E7E3 +0208 E7E3 77 LD (HL),A +0209 E7E4 23 INC HL +0210 E7E5 05 dec b +0211 E7E6 20 F3 JR NZ, wBrdByte +0212 E7E8 +0213 E7E8 0D dec c +0214 E7E9 20 EE JR NZ,wBrd4secs +0215 E7EB +0216 E7EB 3A C9 FB LD A,(hstsec) +0217 E7EE 3C INC A +0218 E7EF 32 C9 FB LD (hstsec),A +0219 E7F2 +0220 E7F2 C1 POP BC +0221 E7F3 +0222 E7F3 10 D2 DJNZ wbRdAllSecs +0223 E7F5 ;================================================================================================ +0224 E7F5 ; Common code for cold and warm boot +0225 E7F5 ;================================================================================================ +0226 E7F5 +0227 E7F5 gocpm: +0228 E7F5 AF xor a ;0 to accumulator +0229 E7F6 32 CB FB ld (hstact),a ;host buffer inactive +0230 E7F9 32 CD FB ld (unacnt),a ;clear unalloc count +0231 E7FC +0232 E7FC 21 80 00 LD HL,tpabuf ; Address of BIOS DMA buffer. +0233 E7FF 22 D6 FB LD (dmaAddr),HL +0234 E802 3E C3 LD A,0C3h ; Opcode for 'JP'. +0235 E804 32 00 00 LD (00h),A ; Load at start of RAM. +0236 E807 21 03 E6 LD HL,wboote ; Address of jump for a warm boot. +0237 E80A 22 01 00 LD (01h),HL +0238 E80D 32 05 00 LD (05h),A ; Opcode for 'JP'. +0239 E810 21 06 D8 LD HL,bdos ; Address of jump for the BDOS. +0240 E813 22 06 00 LD (06h),HL +0241 E816 3A 04 00 LD A,(userdrv) ; Save new drive number (0). +0242 E819 4F LD c,A ; Pass drive number in C. +0243 E81A +0244 E81A C3 00 D0 JP ccp ; Start CP/M by jumping to the CCP. +0245 E81D +0246 E81D ;================================================================================================ +0247 E81D ; Console I/O routines +0248 E81D ;================================================================================================ +0249 E81D +0250 E81D +0251 E81D ;------------------------------------------------------------------------------------------------ +0252 E81D const: +0253 E81D 3A 03 00 LD A,(iobyte) +0254 E820 E6 0B AND 00001011b ; Mask off console and high bit of reader +0255 E822 FE 0A CP 00001010b ; redirected to reader on UR1/2 (Serial A) +0256 E824 28 0A JR Z,constA +0257 E826 FE 02 CP 00000010b ; redirected to reader on TTY/RDR (Serial B) +0258 E828 28 14 JR Z,constB +0259 E82A +0260 E82A E6 03 AND $03 ; remove the reader from the mask - only console bits then remain +0261 E82C FE 01 CP $01 +0262 E82E 20 0E JR NZ,constB +0263 E830 constA: +0264 E830 DB 80 IN A,(ACIA0_C) ; Status byte +0265 E832 E6 01 AND $01 +0266 E834 FE 00 CP $0 ; Z flag set if no char +0267 E836 28 03 JR Z, dataAEmpty +0268 E838 3E FF LD A,0FFH +0269 E83A C9 RET +0270 E83B dataAEmpty: +0271 E83B 3E 00 LD A,0 +0272 E83D C9 RET +0273 E83E +0274 E83E +0275 E83E constB: +0276 E83E DB 82 IN A,(ACIA1_C) ; Status byte +0277 E840 E6 01 AND $01 +0278 E842 FE 00 CP $0 ; Z flag set if no char +0279 E844 28 03 JR Z, dataBEmpty +0280 E846 3E FF LD A,0FFH +0281 E848 C9 RET +0282 E849 dataBEmpty: +0283 E849 3E 00 LD A,0 +0284 E84B C9 RET +0285 E84C +0286 E84C ;------------------------------------------------------------------------------------------------ +0287 E84C reader: +0288 E84C F5 PUSH AF +0289 E84D 3A 03 00 reader2: LD A,(iobyte) +0290 E850 E6 08 AND $08 +0291 E852 FE 08 CP $08 +0292 E854 20 1C JR NZ,coninB +0293 E856 18 0E JR coninA +0294 E858 ;------------------------------------------------------------------------------------------------ +0295 E858 conin: +0296 E858 F5 PUSH AF +0297 E859 3A 03 00 LD A,(iobyte) +0298 E85C E6 03 AND $03 +0299 E85E FE 02 CP $02 +0300 E860 28 EB JR Z,reader2 ; "BAT:" redirect +0301 E862 FE 01 CP $01 +0302 E864 20 0C JR NZ,coninB +0303 E866 +0304 E866 +0305 E866 coninA: +0306 E866 F1 POP AF +0307 E867 waitForCharA: +0308 E867 DB 80 IN A,(ACIA0_C) ; Status byte +0309 E869 E6 01 AND $01 +0310 E86B FE 00 CP $0 ; Z flag set if no char +0311 E86D 28 F8 JR Z, waitForCharA +0312 E86F DB 81 IN A,(ACIA0_D) +0313 E871 +0314 E871 C9 RET ; Char ready in A +0315 E872 +0316 E872 +0317 E872 coninB: +0318 E872 F1 POP AF +0319 E873 waitForCharB: +0320 E873 DB 82 IN A,(ACIA1_C) ; Status byte +0321 E875 E6 01 AND $01 +0322 E877 FE 00 CP $0 ; Z flag set if no char +0323 E879 28 F8 JR Z, waitForCharB +0324 E87B DB 83 IN A,(ACIA1_D) +0325 E87D +0326 E87D C9 RET ; Char ready in A +0327 E87E +0328 E87E ;------------------------------------------------------------------------------------------------ +0329 E87E F5 list: PUSH AF ; Store character +0330 E87F 3A 03 00 list2: LD A,(iobyte) +0331 E882 E6 C0 AND $C0 +0332 E884 FE 40 CP $40 +0333 E886 20 26 JR NZ,conoutB1 +0334 E888 18 1A JR conoutA1 +0335 E88A +0336 E88A ;------------------------------------------------------------------------------------------------ +0337 E88A F5 punch: PUSH AF ; Store character +0338 E88B 3A 03 00 LD A,(iobyte) +0339 E88E E6 20 AND $20 +0340 E890 FE 20 CP $20 +0341 E892 20 1A JR NZ,conoutB1 +0342 E894 18 0E JR conoutA1 +0343 E896 +0344 E896 ;------------------------------------------------------------------------------------------------ +0345 E896 F5 conout: PUSH AF +0346 E897 3A 03 00 LD A,(iobyte) +0347 E89A E6 03 AND $03 +0348 E89C FE 02 CP $02 +0349 E89E 28 DF JR Z,list2 ; "BAT:" redirect +0350 E8A0 FE 01 CP $01 +0351 E8A2 20 0A JR NZ,conoutB1 +0352 E8A4 +0353 E8A4 CD B8 E8 conoutA1: CALL CKACIA0 ; See if ACIA channel A is finished transmitting +0354 E8A7 28 FB JR Z,conoutA1 ; Loop until ACIA flag signals ready +0355 E8A9 79 LD A,C +0356 E8AA D3 81 OUT (ACIA0_D),A ; OUTput the character +0357 E8AC F1 POP AF +0358 E8AD C9 RET +0359 E8AE +0360 E8AE CD BE E8 conoutB1: CALL CKACIA1 ; See if ACIA channel B is finished transmitting +0361 E8B1 28 FB JR Z,conoutB1 ; Loop until ACIA flag signals ready +0362 E8B3 79 LD A,C +0363 E8B4 D3 83 OUT (ACIA1_D),A ; OUTput the character +0364 E8B6 F1 POP AF +0365 E8B7 C9 RET +0366 E8B8 +0367 E8B8 ;------------------------------------------------------------------------------------------------ +0368 E8B8 CKACIA0 +0369 E8B8 DB 80 IN A,(ACIA0_C) ; Status byte D1=TX Buff Empty, D0=RX char ready +0370 E8BA 0F RRCA ; Rotates RX status into Carry Flag, +0371 E8BB CB 47 BIT 0,A ; Set Zero flag if still transmitting character +0372 E8BD C9 RET +0373 E8BE +0374 E8BE CKACIA1 +0375 E8BE DB 82 IN A,(ACIA1_C) ; Status byte D1=TX Buff Empty, D0=RX char ready +0376 E8C0 0F RRCA ; Rotates RX status into Carry Flag, +0377 E8C1 CB 47 BIT 0,A ; Set Zero flag if still transmitting character +0378 E8C3 C9 RET +0379 E8C4 +0380 E8C4 ;------------------------------------------------------------------------------------------------ +0381 E8C4 3E FF listst: LD A,$FF ; Return list status of 0xFF (ready). +0382 E8C6 C9 RET +0383 E8C7 +0384 E8C7 ;================================================================================================ +0385 E8C7 ; Disk processing entry points +0386 E8C7 ;================================================================================================ +0387 E8C7 +0388 E8C7 seldsk: +0389 E8C7 21 00 00 LD HL,$0000 +0390 E8CA 79 LD A,C +0391 E8CB FE 10 CP 16 ; 16 for 128MB disk, 8 for 64MB disk +0392 E8CD 38 0D jr C,chgdsk ; if invalid drive will give BDOS error +0393 E8CF 3A 04 00 LD A,(userdrv) ; so set the drive back to a: +0394 E8D2 B9 CP C ; If the default disk is not the same as the +0395 E8D3 C0 RET NZ ; selected drive then return, +0396 E8D4 AF XOR A ; else reset default back to a: +0397 E8D5 32 04 00 LD (userdrv),A ; otherwise will be stuck in a loop +0398 E8D8 32 C1 FB LD (sekdsk),A +0399 E8DB C9 ret +0400 E8DC +0401 E8DC 32 C1 FB chgdsk: LD (sekdsk),A +0402 E8DF CB 07 RLC a ;*2 +0403 E8E1 CB 07 RLC a ;*4 +0404 E8E3 CB 07 RLC a ;*8 +0405 E8E5 CB 07 RLC a ;*16 +0406 E8E7 21 33 E6 LD HL,dpbase +0407 E8EA 06 00 LD b,0 +0408 E8EC 4F LD c,A +0409 E8ED 09 ADD HL,BC +0410 E8EE +0411 E8EE C9 RET +0412 E8EF +0413 E8EF ;------------------------------------------------------------------------------------------------ +0414 E8EF home: +0415 E8EF 3A CC FB ld a,(hstwrt) ;check for pending write +0416 E8F2 B7 or a +0417 E8F3 20 03 jr nz,homed +0418 E8F5 32 CB FB ld (hstact),a ;clear host active flag +0419 E8F8 homed: +0420 E8F8 01 00 00 LD BC,0000h +0421 E8FB +0422 E8FB ;------------------------------------------------------------------------------------------------ +0423 E8FB ED 43 C2 FB settrk: LD (sektrk),BC ; Set track passed from BDOS in register BC. +0424 E8FF C9 RET +0425 E900 +0426 E900 ;------------------------------------------------------------------------------------------------ +0427 E900 ED 43 C4 FB setsec: LD (seksec),BC ; Set sector passed from BDOS in register BC. +0428 E904 C9 RET +0429 E905 +0430 E905 ;------------------------------------------------------------------------------------------------ +0431 E905 ED 43 D6 FB setdma: LD (dmaAddr),BC ; Set DMA ADDress given by registers BC. +0432 E909 C9 RET +0433 E90A +0434 E90A ;------------------------------------------------------------------------------------------------ +0435 E90A C5 sectran: PUSH BC +0436 E90B E1 POP HL +0437 E90C C9 RET +0438 E90D +0439 E90D ;------------------------------------------------------------------------------------------------ +0440 E90D read: +0441 E90D ;read the selected CP/M sector +0442 E90D AF xor a +0443 E90E 32 CD FB ld (unacnt),a +0444 E911 3E 01 ld a,1 +0445 E913 32 D4 FB ld (readop),a ;read operation +0446 E916 32 D3 FB ld (rsflag),a ;must read data +0447 E919 3E 02 ld a,wrual +0448 E91B 32 D5 FB ld (wrtype),a ;treat as unalloc +0449 E91E C3 88 E9 jp rwoper ;to perform the read +0450 E921 +0451 E921 +0452 E921 ;------------------------------------------------------------------------------------------------ +0453 E921 write: +0454 E921 ;write the selected CP/M sector +0455 E921 AF xor a ;0 to accumulator +0456 E922 32 D4 FB ld (readop),a ;not a read operation +0457 E925 79 ld a,c ;write type in c +0458 E926 32 D5 FB ld (wrtype),a +0459 E929 FE 02 cp wrual ;write unallocated? +0460 E92B 20 17 jr nz,chkuna ;check for unalloc +0461 E92D ; +0462 E92D ; write to unallocated, set parameters +0463 E92D 3E 20 ld a,blksiz/128 ;next unalloc recs +0464 E92F 32 CD FB ld (unacnt),a +0465 E932 3A C1 FB ld a,(sekdsk) ;disk to seek +0466 E935 32 CE FB ld (unadsk),a ;unadsk = sekdsk +0467 E938 2A C2 FB ld hl,(sektrk) +0468 E93B 22 CF FB ld (unatrk),hl ;unatrk = sectrk +0469 E93E 3A C4 FB ld a,(seksec) +0470 E941 32 D1 FB ld (unasec),a ;unasec = seksec +0471 E944 ; +0472 E944 chkuna: +0473 E944 ; check for write to unallocated sector +0474 E944 3A CD FB ld a,(unacnt) ;any unalloc remain? +0475 E947 B7 or a +0476 E948 28 36 jr z,alloc ;skip if not +0477 E94A ; +0478 E94A ; more unallocated records remain +0479 E94A 3D dec a ;unacnt = unacnt-1 +0480 E94B 32 CD FB ld (unacnt),a +0481 E94E 3A C1 FB ld a,(sekdsk) ;same disk? +0482 E951 21 CE FB ld hl,unadsk +0483 E954 BE cp (hl) ;sekdsk = unadsk? +0484 E955 C2 80 E9 jp nz,alloc ;skip if not +0485 E958 ; +0486 E958 ; disks are the same +0487 E958 21 CF FB ld hl,unatrk +0488 E95B CD 1F EA call sektrkcmp ;sektrk = unatrk? +0489 E95E C2 80 E9 jp nz,alloc ;skip if not +0490 E961 ; +0491 E961 ; tracks are the same +0492 E961 3A C4 FB ld a,(seksec) ;same sector? +0493 E964 21 D1 FB ld hl,unasec +0494 E967 BE cp (hl) ;seksec = unasec? +0495 E968 C2 80 E9 jp nz,alloc ;skip if not +0496 E96B ; +0497 E96B ; match, move to next sector for future ref +0498 E96B 34 inc (hl) ;unasec = unasec+1 +0499 E96C 7E ld a,(hl) ;end of track? +0500 E96D FE 80 cp cpmspt ;count CP/M sectors +0501 E96F 38 09 jr c,noovf ;skip if no overflow +0502 E971 ; +0503 E971 ; overflow to next track +0504 E971 36 00 ld (hl),0 ;unasec = 0 +0505 E973 2A CF FB ld hl,(unatrk) +0506 E976 23 inc hl +0507 E977 22 CF FB ld (unatrk),hl ;unatrk = unatrk+1 +0508 E97A ; +0509 E97A noovf: +0510 E97A ;match found, mark as unnecessary read +0511 E97A AF xor a ;0 to accumulator +0512 E97B 32 D3 FB ld (rsflag),a ;rsflag = 0 +0513 E97E 18 08 jr rwoper ;to perform the write +0514 E980 ; +0515 E980 alloc: +0516 E980 ;not an unallocated record, requires pre-read +0517 E980 AF xor a ;0 to accum +0518 E981 32 CD FB ld (unacnt),a ;unacnt = 0 +0519 E984 3C inc a ;1 to accum +0520 E985 32 D3 FB ld (rsflag),a ;rsflag = 1 +0521 E988 +0522 E988 ;------------------------------------------------------------------------------------------------ +0523 E988 rwoper: +0524 E988 ;enter here to perform the read/write +0525 E988 AF xor a ;zero to accum +0526 E989 32 D2 FB ld (erflag),a ;no errors (yet) +0527 E98C 3A C4 FB ld a,(seksec) ;compute host sector +0528 E98F B7 or a ;carry = 0 +0529 E990 1F rra ;shift right +0530 E991 B7 or a ;carry = 0 +0531 E992 1F rra ;shift right +0532 E993 32 CA FB ld (sekhst),a ;host sector to seek +0533 E996 ; +0534 E996 ; active host sector? +0535 E996 21 CB FB ld hl,hstact ;host active flag +0536 E999 7E ld a,(hl) +0537 E99A 36 01 ld (hl),1 ;always becomes 1 +0538 E99C B7 or a ;was it already? +0539 E99D 28 21 jr z,filhst ;fill host if not +0540 E99F ; +0541 E99F ; host buffer active, same as seek buffer? +0542 E99F 3A C1 FB ld a,(sekdsk) +0543 E9A2 21 C6 FB ld hl,hstdsk ;same disk? +0544 E9A5 BE cp (hl) ;sekdsk = hstdsk? +0545 E9A6 20 11 jr nz,nomatch +0546 E9A8 ; +0547 E9A8 ; same disk, same track? +0548 E9A8 21 C7 FB ld hl,hsttrk +0549 E9AB CD 1F EA call sektrkcmp ;sektrk = hsttrk? +0550 E9AE 20 09 jr nz,nomatch +0551 E9B0 ; +0552 E9B0 ; same disk, same track, same buffer? +0553 E9B0 3A CA FB ld a,(sekhst) +0554 E9B3 21 C9 FB ld hl,hstsec ;sekhst = hstsec? +0555 E9B6 BE cp (hl) +0556 E9B7 28 24 jr z,match ;skip if match +0557 E9B9 ; +0558 E9B9 nomatch: +0559 E9B9 ;proper disk, but not correct sector +0560 E9B9 3A CC FB ld a,(hstwrt) ;host written? +0561 E9BC B7 or a +0562 E9BD C4 C4 EA call nz,writehst ;clear host buff +0563 E9C0 ; +0564 E9C0 filhst: +0565 E9C0 ;may have to fill the host buffer +0566 E9C0 3A C1 FB ld a,(sekdsk) +0567 E9C3 32 C6 FB ld (hstdsk),a +0568 E9C6 2A C2 FB ld hl,(sektrk) +0569 E9C9 22 C7 FB ld (hsttrk),hl +0570 E9CC 3A CA FB ld a,(sekhst) +0571 E9CF 32 C9 FB ld (hstsec),a +0572 E9D2 3A D3 FB ld a,(rsflag) ;need to read? +0573 E9D5 B7 or a +0574 E9D6 C4 95 EA call nz,readhst ;yes, if 1 +0575 E9D9 AF xor a ;0 to accum +0576 E9DA 32 CC FB ld (hstwrt),a ;no pending write +0577 E9DD ; +0578 E9DD match: +0579 E9DD ;copy data to or from buffer +0580 E9DD 3A C4 FB ld a,(seksec) ;mask buffer number +0581 E9E0 E6 03 and secmsk ;least signif bits +0582 E9E2 6F ld l,a ;ready to shift +0583 E9E3 26 00 ld h,0 ;double count +0584 E9E5 29 add hl,hl +0585 E9E6 29 add hl,hl +0586 E9E7 29 add hl,hl +0587 E9E8 29 add hl,hl +0588 E9E9 29 add hl,hl +0589 E9EA 29 add hl,hl +0590 E9EB 29 add hl,hl +0591 E9EC ; hl has relative host buffer address +0592 E9EC 11 D8 FB ld de,hstbuf +0593 E9EF 19 add hl,de ;hl = host address +0594 E9F0 EB ex de,hl ;now in DE +0595 E9F1 2A D6 FB ld hl,(dmaAddr) ;get/put CP/M data +0596 E9F4 0E 80 ld c,128 ;length of move +0597 E9F6 3A D4 FB ld a,(readop) ;which way? +0598 E9F9 B7 or a +0599 E9FA 20 06 jr nz,rwmove ;skip if read +0600 E9FC ; +0601 E9FC ; write operation, mark and switch direction +0602 E9FC 3E 01 ld a,1 +0603 E9FE 32 CC FB ld (hstwrt),a ;hstwrt = 1 +0604 EA01 EB ex de,hl ;source/dest swap +0605 EA02 ; +0606 EA02 rwmove: +0607 EA02 ;C initially 128, DE is source, HL is dest +0608 EA02 1A ld a,(de) ;source character +0609 EA03 13 inc de +0610 EA04 77 ld (hl),a ;to dest +0611 EA05 23 inc hl +0612 EA06 0D dec c ;loop 128 times +0613 EA07 20 F9 jr nz,rwmove +0614 EA09 ; +0615 EA09 ; data has been moved to/from host buffer +0616 EA09 3A D5 FB ld a,(wrtype) ;write type +0617 EA0C FE 01 cp wrdir ;to directory? +0618 EA0E 3A D2 FB ld a,(erflag) ;in case of errors +0619 EA11 C0 ret nz ;no further processing +0620 EA12 ; +0621 EA12 ; clear host buffer for directory write +0622 EA12 B7 or a ;errors? +0623 EA13 C0 ret nz ;skip if so +0624 EA14 AF xor a ;0 to accum +0625 EA15 32 CC FB ld (hstwrt),a ;buffer written +0626 EA18 CD C4 EA call writehst +0627 EA1B 3A D2 FB ld a,(erflag) +0628 EA1E C9 ret +0629 EA1F +0630 EA1F ;------------------------------------------------------------------------------------------------ +0631 EA1F ;Utility subroutine for 16-bit compare +0632 EA1F sektrkcmp: +0633 EA1F ;HL = .unatrk or .hsttrk, compare with sektrk +0634 EA1F EB ex de,hl +0635 EA20 21 C2 FB ld hl,sektrk +0636 EA23 1A ld a,(de) ;low byte compare +0637 EA24 BE cp (HL) ;same? +0638 EA25 C0 ret nz ;return if not +0639 EA26 ; low bytes equal, test high 1s +0640 EA26 13 inc de +0641 EA27 23 inc hl +0642 EA28 1A ld a,(de) +0643 EA29 BE cp (hl) ;sets flags +0644 EA2A C9 ret +0645 EA2B +0646 EA2B ;================================================================================================ +0647 EA2B ; Convert track/head/sector into LBA for physical access to the disk +0648 EA2B ;================================================================================================ +0649 EA2B setLBAaddr: +0650 EA2B 2A C7 FB LD HL,(hsttrk) +0651 EA2E CB 05 RLC L +0652 EA30 CB 05 RLC L +0653 EA32 CB 05 RLC L +0654 EA34 CB 05 RLC L +0655 EA36 CB 05 RLC L +0656 EA38 7D LD A,L +0657 EA39 E6 E0 AND 0E0H +0658 EA3B 6F LD L,A +0659 EA3C 3A C9 FB LD A,(hstsec) +0660 EA3F 85 ADD A,L +0661 EA40 32 9D FB LD (lba0),A +0662 EA43 +0663 EA43 2A C7 FB LD HL,(hsttrk) +0664 EA46 CB 0D RRC L +0665 EA48 CB 0D RRC L +0666 EA4A CB 0D RRC L +0667 EA4C 7D LD A,L +0668 EA4D E6 1F AND 01FH +0669 EA4F 6F LD L,A +0670 EA50 CB 04 RLC H +0671 EA52 CB 04 RLC H +0672 EA54 CB 04 RLC H +0673 EA56 CB 04 RLC H +0674 EA58 CB 04 RLC H +0675 EA5A 7C LD A,H +0676 EA5B E6 20 AND 020H +0677 EA5D 67 LD H,A +0678 EA5E 3A C6 FB LD A,(hstdsk) +0679 EA61 CB 07 RLC a +0680 EA63 CB 07 RLC a +0681 EA65 CB 07 RLC a +0682 EA67 CB 07 RLC a +0683 EA69 CB 07 RLC a +0684 EA6B CB 07 RLC a +0685 EA6D E6 C0 AND 0C0H +0686 EA6F 84 ADD A,H +0687 EA70 85 ADD A,L +0688 EA71 32 9E FB LD (lba1),A +0689 EA74 +0690 EA74 3A C6 FB LD A,(hstdsk) +0691 EA77 CB 0F RRC A +0692 EA79 CB 0F RRC A +0693 EA7B E6 03 AND 03H +0694 EA7D 32 9F FB LD (lba2),A +0695 EA80 +0696 EA80 3E 00 LD a,00H +0697 EA82 32 A0 FB LD (lba3),A +0698 EA85 +0699 EA85 ; Transfer LBA to disk (LBA3 not used on SD card) +0700 EA85 3A 9F FB LD A,(lba2) +0701 EA88 D3 8C OUT (SD_LBA2),A +0702 EA8A 3A 9E FB LD A,(lba1) +0703 EA8D D3 8B OUT (SD_LBA1),A +0704 EA8F 3A 9D FB LD A,(lba0) +0705 EA92 D3 8A OUT (SD_LBA0),A +0706 EA94 C9 RET +0707 EA95 +0708 EA95 ;================================================================================================ +0709 EA95 ; Read physical sector from host +0710 EA95 ;================================================================================================ +0711 EA95 +0712 EA95 readhst: +0713 EA95 F5 PUSH AF +0714 EA96 C5 PUSH BC +0715 EA97 E5 PUSH HL +0716 EA98 +0717 EA98 DB 89 rdWait1: IN A,(SD_STATUS) +0718 EA9A FE 80 CP 128 +0719 EA9C 20 FA JR NZ,rdWait1 +0720 EA9E +0721 EA9E CD 2B EA CALL setLBAaddr +0722 EAA1 +0723 EAA1 3E 00 LD A,$00 ; 00 = Read block +0724 EAA3 D3 89 OUT (SD_CONTROL),A +0725 EAA5 +0726 EAA5 0E 04 LD c,4 +0727 EAA7 21 D8 FB LD HL,hstbuf +0728 EAAA rd4secs: +0729 EAAA 06 80 LD b,128 +0730 EAAC rdByte: +0731 EAAC +0732 EAAC DB 89 rdWait2: IN A,(SD_STATUS) +0733 EAAE FE E0 CP 224 ; Read byte waiting +0734 EAB0 20 FA JR NZ,rdWait2 +0735 EAB2 +0736 EAB2 DB 88 IN A,(SD_DATA) +0737 EAB4 +0738 EAB4 77 LD (HL),A +0739 EAB5 23 INC HL +0740 EAB6 05 dec b +0741 EAB7 20 F3 JR NZ, rdByte +0742 EAB9 0D dec c +0743 EABA 20 EE JR NZ,rd4secs +0744 EABC +0745 EABC E1 POP HL +0746 EABD C1 POP BC +0747 EABE F1 POP AF +0748 EABF +0749 EABF AF XOR a +0750 EAC0 32 D2 FB ld (erflag),a +0751 EAC3 C9 RET +0752 EAC4 +0753 EAC4 +0754 EAC4 ;================================================================================================ +0755 EAC4 ; Write physical sector to host +0756 EAC4 ;================================================================================================ +0757 EAC4 +0758 EAC4 writehst: +0759 EAC4 F5 PUSH AF +0760 EAC5 C5 PUSH BC +0761 EAC6 E5 PUSH HL +0762 EAC7 +0763 EAC7 DB 89 wrWait1: IN A,(SD_STATUS) +0764 EAC9 FE 80 CP 128 +0765 EACB 20 FA JR NZ,wrWait1 +0766 EACD +0767 EACD CD 2B EA CALL setLBAaddr +0768 EAD0 +0769 EAD0 3E 01 LD A,$01 ; 01 = Write block +0770 EAD2 D3 89 OUT (SD_CONTROL),A +0771 EAD4 +0772 EAD4 0E 04 LD c,4 +0773 EAD6 21 D8 FB LD HL,hstbuf +0774 EAD9 wr4secs: +0775 EAD9 06 80 LD b,128 +0776 EADB wrByte: +0777 EADB +0778 EADB DB 89 wrWait2: IN A,(SD_STATUS) +0779 EADD FE A0 CP 160 ; Write buffer empty +0780 EADF 20 FA JR NZ,wrWait2 +0781 EAE1 +0782 EAE1 ; UPDATE S0urceror, inserted wait cycle between IN and OUT +0783 EAE1 ; to resolve unknown write issue in sd_controller.vhd in combination +0784 EAE1 ; with MISTer virtual SD interface sys/sd_card.sv +0785 EAE1 ; which results in hangs or write errors. +0786 EAE1 C5 push bc +0787 EAE2 06 64 ld b,100 +0788 EAE4 _again: +0789 EAE4 10 FE djnz _again +0790 EAE6 C1 pop bc +0791 EAE7 ; END UPDATE +0792 EAE7 +0793 EAE7 7E LD A,(HL) +0794 EAE8 D3 88 OUT (SD_DATA),A +0795 EAEA 23 INC HL +0796 EAEB 05 dec b +0797 EAEC 20 ED JR NZ, wrByte +0798 EAEE +0799 EAEE 0D dec c +0800 EAEF 20 E8 JR NZ,wr4secs +0801 EAF1 +0802 EAF1 E1 POP HL +0803 EAF2 C1 POP BC +0804 EAF3 F1 POP AF +0805 EAF4 +0806 EAF4 AF XOR a +0807 EAF5 32 D2 FB ld (erflag),a +0808 EAF8 C9 RET +0809 EAF9 +0810 EAF9 ;================================================================================================ +0811 EAF9 ; Utilities +0812 EAF9 ;================================================================================================ +0813 EAF9 +0814 EAF9 printInline: +0815 EAF9 E3 EX (SP),HL ; PUSH HL and put RET ADDress into HL +0816 EAFA F5 PUSH AF +0817 EAFB C5 PUSH BC +0818 EAFC 7E nextILChar: LD A,(HL) +0819 EAFD FE 00 CP 0 +0820 EAFF 28 07 JR Z,endOfPrint +0821 EB01 4F LD C,A +0822 EB02 CD 96 E8 CALL conout ; Print to TTY +0823 EB05 23 iNC HL +0824 EB06 18 F4 JR nextILChar +0825 EB08 23 endOfPrint: INC HL ; Get past "null" terminator +0826 EB09 C1 POP BC +0827 EB0A F1 POP AF +0828 EB0B E3 EX (SP),HL ; PUSH new RET ADDress on stack and restore HL +0829 EB0C C9 RET +0830 EB0D +0831 EB0D ;================================================================================================ +0832 EB0D ; Data storage +0833 EB0D ;================================================================================================ +0834 EB0D +0835 EB0D dirbuf: .ds 128 ;scratch directory area +0836 EB8D alv00: .ds 257 ;allocation vector 0 +0837 EC8E alv01: .ds 257 ;allocation vector 1 +0838 ED8F alv02: .ds 257 ;allocation vector 2 +0839 EE90 alv03: .ds 257 ;allocation vector 3 +0840 EF91 alv04: .ds 257 ;allocation vector 4 +0841 F092 alv05: .ds 257 ;allocation vector 5 +0842 F193 alv06: .ds 257 ;allocation vector 6 +0843 F294 alv07: .ds 257 ;allocation vector 7 +0844 F395 alv08: .ds 257 ;allocation vector 8 +0845 F496 alv09: .ds 257 ;allocation vector 9 +0846 F597 alv10: .ds 257 ;allocation vector 10 +0847 F698 alv11: .ds 257 ;allocation vector 11 +0848 F799 alv12: .ds 257 ;allocation vector 12 +0849 F89A alv13: .ds 257 ;allocation vector 13 +0850 F99B alv14: .ds 257 ;allocation vector 14 +0851 FA9C alv15: .ds 257 ;allocation vector 15 +0852 FB9D +0853 FB9D 00 lba0 .DB 00h +0854 FB9E 00 lba1 .DB 00h +0855 FB9F 00 lba2 .DB 00h +0856 FBA0 00 lba3 .DB 00h +0857 FBA1 +0858 FBA1 .DS 020h ; Start of BIOS stack area. +0859 FBC1 biosstack: .EQU $ +0860 FBC1 +0861 FBC1 sekdsk: .ds 1 ;seek disk number +0862 FBC2 sektrk: .ds 2 ;seek track number +0863 FBC4 seksec: .ds 2 ;seek sector number +0864 FBC6 ; +0865 FBC6 hstdsk: .ds 1 ;host disk number +0866 FBC7 hsttrk: .ds 2 ;host track number +0867 FBC9 hstsec: .ds 1 ;host sector number +0868 FBCA ; +0869 FBCA sekhst: .ds 1 ;seek shr secshf +0870 FBCB hstact: .ds 1 ;host active flag +0871 FBCC hstwrt: .ds 1 ;host written flag +0872 FBCD ; +0873 FBCD unacnt: .ds 1 ;unalloc rec cnt +0874 FBCE unadsk: .ds 1 ;last unalloc disk +0875 FBCF unatrk: .ds 2 ;last unalloc track +0876 FBD1 unasec: .ds 1 ;last unalloc sector +0877 FBD2 ; +0878 FBD2 erflag: .ds 1 ;error reporting +0879 FBD3 rsflag: .ds 1 ;read sector flag +0880 FBD4 readop: .ds 1 ;1 if read operation +0881 FBD5 wrtype: .ds 1 ;write operation type +0882 FBD6 dmaAddr: .ds 2 ;last dma address +0883 FBD8 hstbuf: .ds 512 ;host buffer +0884 FDD8 +0885 FDD8 hstBufEnd: .EQU $ +0886 FDD8 +0887 FDD8 biosEnd: .EQU $ +0888 FDD8 +0889 FDD8 ; Disable the ROM, pop the active IO port from the stack (supplied by monitor), +0890 FDD8 ; then start CP/M +0891 FDD8 popAndRun: +0892 FDD8 3E 01 LD A,$01 +0893 FDDA D3 38 OUT ($38),A +0894 FDDC +0895 FDDC F1 POP AF +0896 FDDD FE 01 CP $01 +0897 FDDF 28 04 JR Z,consoleAtB +0898 FDE1 3E 01 LD A,$01 ;(List is TTY:, Punch is TTY:, Reader is TTY:, Console is CRT:) +0899 FDE3 18 02 JR setIOByte +0900 FDE5 3E 00 consoleAtB: LD A,$00 ;(List is TTY:, Punch is TTY:, Reader is TTY:, Console is TTY:) +0901 FDE7 32 03 00 setIOByte: LD (iobyte),A +0902 FDEA C3 00 E6 JP bios +0903 FDED +0904 FDED +0905 FDED ;================================================================================= +0906 FDED ; Relocate TPA area from 4100 to 0100 then start CP/M +0907 FDED ; Used to manually transfer a loaded program after CP/M was previously loaded +0908 FDED ;================================================================================= +0909 FDED +0910 FFE8 .org 0FFE8H +0911 FFE8 3E 01 LD A,$01 +0912 FFEA D3 38 OUT ($38),A +0913 FFEC +0914 FFEC 21 00 41 LD HL,04100H +0915 FFEF 11 00 01 LD DE,00100H +0916 FFF2 01 00 8F LD BC,08F00H +0917 FFF5 ED B0 LDIR +0918 FFF7 C3 00 E6 JP bios +0919 FFFA +0920 FFFA ;================================================================================= +0921 FFFA ; Normal start CP/M vector +0922 FFFA ;================================================================================= +0923 FFFA +0924 FFFE .ORG 0FFFEH +0925 FFFE D8 FD .dw popAndRun +0926 0000 +0927 0000 .END +tasm: Number of errors = 0 diff --git a/Z80 CPM and bootloader (basmon)/source/CH376S~1.LST b/Z80 CPM and bootloader (basmon)/source/CH376S~1.LST new file mode 100644 index 0000000..829b8a9 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/CH376S~1.LST @@ -0,0 +1,88 @@ +0001 0000 LF .EQU 0AH ;line feed +0002 0000 FF .EQU 0CH ;form feed +0003 0000 CR .EQU 0DH ;carriage RETurn +0004 0000 DOT .EQU '.' +0005 0000 CH375_CMD_CHECK_EXIST .EQU 06H +0006 0000 CH375_CMD_RESET_ALL .EQU 05H +0007 0000 +0008 4000 .ORG 4000H +0009 4000 +0010 4000 CD 64 40 CALL printInline +0011 4003 436865636B20 .TEXT "Check CH376s communication" +0011 4009 43483337367320636F6D6D756E69636174696F6E +0012 401D 0D 0A 00 .DB CR,LF,0 +0013 4020 +0014 4020 CD 64 40 CALL printInline +0015 4023 53656E642041 .TEXT "Send A" +0016 4029 0D 0A 00 .DB CR,LF,0 +0017 402C +0018 402C 3E 05 ld a, CH375_CMD_RESET_ALL +0019 402E D3 20 out (20h),a +0020 4030 +0021 4030 3E 06 ld a, CH375_CMD_CHECK_EXIST +0022 4032 D3 20 out (20h),a +0023 4034 3E 41 ld a, 'A' +0024 4036 D3 20 out (20h),a +0025 4038 ; receive result +0026 4038 AF xor a +0027 4039 D3 20 out (20h),a +0028 403B DB 20 in a, (20h) +0029 403D EE FF xor 255 +0030 403F +0031 403F CD 64 40 CALL printInline +0032 4042 526563656976 .TEXT "Received " +0032 4048 656420 +0033 404B 00 .DB 0 +0034 404C +0035 404C CF RST 08H ; print contents of A +0036 404D +0037 404D CD 64 40 CALL printInline +0038 4050 0D 0A 00 .DB CR,LF,0 +0039 4053 +0040 4053 C9 ret +0041 4054 +0042 4054 ; LOOPBACK TEST +0043 4054 +0044 4054 06 39 ld b, 39h +0045 4056 outer: +0046 4056 78 ld a, b +0047 4057 FE 2F cp 2fh +0048 4059 C8 ret z +0049 405A ; send out +0050 405A D3 20 out (20h),a +0051 405C ;inner: +0052 405C ; ld a, DOT +0053 405C ; rst 08h +0054 405C ; in a, (21h) +0055 405C ; bit 0,a +0056 405C ; jr z, inner +0057 405C AF xor a +0058 405D +0059 405D ; read back +0060 405D DB 20 in a, (20h) +0061 405F CF rst 08h ; should be 30h => 0..9 +0062 4060 +0063 4060 05 dec b +0064 4061 18 F3 jr outer +0065 4063 +0066 4063 C9 ret +0067 4064 +0068 4064 printInline: +0069 4064 E3 EX (SP),HL ; PUSH HL and put RET ADDress into HL +0070 4065 F5 PUSH AF +0071 4066 C5 PUSH BC +0072 4067 7E nextILChar: LD A,(HL) +0073 4068 FE 00 CP 0 +0074 406A 28 04 JR Z,endOfPrint +0075 406C CF RST 08H +0076 406D 23 INC HL +0077 406E 18 F7 JR nextILChar +0078 4070 23 endOfPrint: INC HL ; Get past "null" terminator +0079 4071 C1 POP BC +0080 4072 F1 POP AF +0081 4073 E3 EX (SP),HL ; PUSH new RET ADDress on stack and restore HL +0082 4074 C9 RET +0083 4075 +0084 4075 +0085 4075 .END +tasm: Number of errors = 0 diff --git a/Z80 CPM and bootloader (basmon)/source/CPM22.LST b/Z80 CPM and bootloader (basmon)/source/CPM22.LST new file mode 100644 index 0000000..8bcb096 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/CPM22.LST @@ -0,0 +1,3781 @@ +0001 0000 ;************************************************************** +0002 0000 ;* +0003 0000 ;* C P / M version 2 . 2 +0004 0000 ;* +0005 0000 ;* Reconstructed from memory image on February 27, 1981 +0006 0000 ;* +0007 0000 ;* by Clark A. Calkins +0008 0000 ;* +0009 0000 ;************************************************************** +0010 0000 ; +0011 0000 ; Set memory limit here. This is the amount of contigeous +0012 0000 ; ram starting from 0000. CP/M will reside at the end of this space. +0013 0000 ; +0014 0000 +0015 0000 IOBYTE .EQU 3 ;i/o definition byte. +0016 0000 TDRIVE .EQU 4 ;current drive name and user number. +0017 0000 ENTRY .EQU 5 ;entry point for the cp/m bdos. +0018 0000 TFCB .EQU 5CH ;default file control block. +0019 0000 TBUFF .EQU 80H ;i/o buffer and command line storage. +0020 0000 TBASE .EQU 100H ;transiant program storage area. +0021 0000 ; +0022 0000 ; Set control character equates. +0023 0000 ; +0024 0000 CNTRLC .EQU 3 ;control-c +0025 0000 CNTRLE .EQU 05H ;control-e +0026 0000 BS .EQU 08H ;backspace +0027 0000 TAB .EQU 09H ;tab +0028 0000 LF .EQU 0AH ;line feed +0029 0000 FF .EQU 0CH ;form feed +0030 0000 CR .EQU 0DH ;carriage return +0031 0000 CNTRLP .EQU 10H ;control-p +0032 0000 CNTRLR .EQU 12H ;control-r +0033 0000 CNTRLS .EQU 13H ;control-s +0034 0000 CNTRLU .EQU 15H ;control-u +0035 0000 CNTRLX .EQU 18H ;control-x +0036 0000 CNTRLZ .EQU 1AH ;control-z (end-of-file mark) +0037 0000 DEL .EQU 7FH ;rubout +0038 0000 ; +0039 0000 ; Set origin for CP/M +0040 0000 ; +0041 D000 .ORG 0D000H +0042 D000 ; +0043 D000 C3 5C D3 CBASE: JP COMMAND ;execute command processor (ccp). +0044 D003 C3 58 D3 JP CLEARBUF ;entry to empty input buffer before starting ccp. +0045 D006 +0046 D006 ; +0047 D006 ; Standard cp/m ccp input buffer. Format is (max length), +0048 D006 ; (actual length), (char #1), (char #2), (char #3), etc. +0049 D006 ; +0050 D006 7F INBUFF: .DB 127 ;length of input buffer. +0051 D007 00 .DB 0 ;current length of contents. +0052 D008 436F70797269 .TEXT "Copyright" +0052 D00E 676874 +0053 D011 203139373920 .TEXT " 1979 (c) by Digital Research " +0053 D017 286329206279204469676974616C205265736561726368202020202020 +0054 D034 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0054 D03A 0000000000000000000000000000000000 +0055 D04B 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0055 D051 0000000000000000000000000000000000 +0056 D062 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0056 D068 0000000000000000000000000000000000 +0057 D079 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0057 D07F 000000000000000000 +0058 D088 08 D0 INPOINT:.DW INBUFF+2 ;input line pointer +0059 D08A 00 00 NAMEPNT:.DW 0 ;input line pointer used for error message. Points to +0060 D08C ; ;start of name in error. +0061 D08C ; +0062 D08C ; Routine to print (A) on the console. All registers used. +0063 D08C ; +0064 D08C 5F PRINT: LD E,A ;setup bdos call. +0065 D08D 0E 02 LD C,2 +0066 D08F C3 05 00 JP ENTRY +0067 D092 ; +0068 D092 ; Routine to print (A) on the console and to save (BC). +0069 D092 ; +0070 D092 C5 PRINTB: PUSH BC +0071 D093 CD 8C D0 CALL PRINT +0072 D096 C1 POP BC +0073 D097 C9 RET +0074 D098 ; +0075 D098 ; Routine to send a carriage return, line feed combination +0076 D098 ; to the console. +0077 D098 ; +0078 D098 3E 0D CRLF: LD A,CR +0079 D09A CD 92 D0 CALL PRINTB +0080 D09D 3E 0A LD A,LF +0081 D09F C3 92 D0 JP PRINTB +0082 D0A2 ; +0083 D0A2 ; Routine to send one space to the console and save (BC). +0084 D0A2 ; +0085 D0A2 3E 20 SPACE: LD A,' ' +0086 D0A4 C3 92 D0 JP PRINTB +0087 D0A7 ; +0088 D0A7 ; Routine to print character string pointed to be (BC) on the +0089 D0A7 ; console. It must terminate with a null byte. +0090 D0A7 ; +0091 D0A7 C5 PLINE: PUSH BC +0092 D0A8 CD 98 D0 CALL CRLF +0093 D0AB E1 POP HL +0094 D0AC 7E PLINE2: LD A,(HL) +0095 D0AD B7 OR A +0096 D0AE C8 RET Z +0097 D0AF 23 INC HL +0098 D0B0 E5 PUSH HL +0099 D0B1 CD 8C D0 CALL PRINT +0100 D0B4 E1 POP HL +0101 D0B5 C3 AC D0 JP PLINE2 +0102 D0B8 ; +0103 D0B8 ; Routine to reset the disk system. +0104 D0B8 ; +0105 D0B8 0E 0D RESDSK: LD C,13 +0106 D0BA C3 05 00 JP ENTRY +0107 D0BD ; +0108 D0BD ; Routine to select disk (A). +0109 D0BD ; +0110 D0BD 5F DSKSEL: LD E,A +0111 D0BE 0E 0E LD C,14 +0112 D0C0 C3 05 00 JP ENTRY +0113 D0C3 ; +0114 D0C3 ; Routine to call bdos and save the return code. The zero +0115 D0C3 ; flag is set on a return of 0ffh. +0116 D0C3 ; +0117 D0C3 CD 05 00 ENTRY1: CALL ENTRY +0118 D0C6 32 EE D7 LD (RTNCODE),A ;save return code. +0119 D0C9 3C INC A ;set zero if 0ffh returned. +0120 D0CA C9 RET +0121 D0CB ; +0122 D0CB ; Routine to open a file. (DE) must point to the FCB. +0123 D0CB ; +0124 D0CB 0E 0F OPEN: LD C,15 +0125 D0CD C3 C3 D0 JP ENTRY1 +0126 D0D0 ; +0127 D0D0 ; Routine to open file at (FCB). +0128 D0D0 ; +0129 D0D0 AF OPENFCB:XOR A ;clear the record number byte at fcb+32 +0130 D0D1 32 ED D7 LD (FCB+32),A +0131 D0D4 11 CD D7 LD DE,FCB +0132 D0D7 C3 CB D0 JP OPEN +0133 D0DA ; +0134 D0DA ; Routine to close a file. (DE) points to FCB. +0135 D0DA ; +0136 D0DA 0E 10 CLOSE: LD C,16 +0137 D0DC C3 C3 D0 JP ENTRY1 +0138 D0DF ; +0139 D0DF ; Routine to search for the first file with ambigueous name +0140 D0DF ; (DE). +0141 D0DF ; +0142 D0DF 0E 11 SRCHFST:LD C,17 +0143 D0E1 C3 C3 D0 JP ENTRY1 +0144 D0E4 ; +0145 D0E4 ; Search for the next ambigeous file name. +0146 D0E4 ; +0147 D0E4 0E 12 SRCHNXT:LD C,18 +0148 D0E6 C3 C3 D0 JP ENTRY1 +0149 D0E9 ; +0150 D0E9 ; Search for file at (FCB). +0151 D0E9 ; +0152 D0E9 11 CD D7 SRCHFCB:LD DE,FCB +0153 D0EC C3 DF D0 JP SRCHFST +0154 D0EF ; +0155 D0EF ; Routine to delete a file pointed to by (DE). +0156 D0EF ; +0157 D0EF 0E 13 DELETE: LD C,19 +0158 D0F1 C3 05 00 JP ENTRY +0159 D0F4 ; +0160 D0F4 ; Routine to call the bdos and set the zero flag if a zero +0161 D0F4 ; status is returned. +0162 D0F4 ; +0163 D0F4 CD 05 00 ENTRY2: CALL ENTRY +0164 D0F7 B7 OR A ;set zero flag if appropriate. +0165 D0F8 C9 RET +0166 D0F9 ; +0167 D0F9 ; Routine to read the next record from a sequential file. +0168 D0F9 ; (DE) points to the FCB. +0169 D0F9 ; +0170 D0F9 0E 14 RDREC: LD C,20 +0171 D0FB C3 F4 D0 JP ENTRY2 +0172 D0FE ; +0173 D0FE ; Routine to read file at (FCB). +0174 D0FE ; +0175 D0FE 11 CD D7 READFCB:LD DE,FCB +0176 D101 C3 F9 D0 JP RDREC +0177 D104 ; +0178 D104 ; Routine to write the next record of a sequential file. +0179 D104 ; (DE) points to the FCB. +0180 D104 ; +0181 D104 0E 15 WRTREC: LD C,21 +0182 D106 C3 F4 D0 JP ENTRY2 +0183 D109 ; +0184 D109 ; Routine to create the file pointed to by (DE). +0185 D109 ; +0186 D109 0E 16 CREATE: LD C,22 +0187 D10B C3 C3 D0 JP ENTRY1 +0188 D10E ; +0189 D10E ; Routine to rename the file pointed to by (DE). Note that +0190 D10E ; the new name starts at (DE+16). +0191 D10E ; +0192 D10E 0E 17 RENAM: LD C,23 +0193 D110 C3 05 00 JP ENTRY +0194 D113 ; +0195 D113 ; Get the current user code. +0196 D113 ; +0197 D113 1E FF GETUSR: LD E,0FFH +0198 D115 ; +0199 D115 ; Routne to get or set the current user code. +0200 D115 ; If (E) is FF then this is a GET, else it is a SET. +0201 D115 ; +0202 D115 0E 20 GETSETUC: LD C,32 +0203 D117 C3 05 00 JP ENTRY +0204 D11A ; +0205 D11A ; Routine to set the current drive byte at (TDRIVE). +0206 D11A ; +0207 D11A CD 13 D1 SETCDRV:CALL GETUSR ;get user number +0208 D11D 87 ADD A,A ;and shift into the upper 4 bits. +0209 D11E 87 ADD A,A +0210 D11F 87 ADD A,A +0211 D120 87 ADD A,A +0212 D121 21 EF D7 LD HL,CDRIVE ;now add in the current drive number. +0213 D124 B6 OR (HL) +0214 D125 32 04 00 LD (TDRIVE),A ;and save. +0215 D128 C9 RET +0216 D129 ; +0217 D129 ; Move currently active drive down to (TDRIVE). +0218 D129 ; +0219 D129 3A EF D7 MOVECD: LD A,(CDRIVE) +0220 D12C 32 04 00 LD (TDRIVE),A +0221 D12F C9 RET +0222 D130 ; +0223 D130 ; Routine to convert (A) into upper case ascii. Only letters +0224 D130 ; are affected. +0225 D130 ; +0226 D130 FE 61 UPPER: CP 'a' ;check for letters in the range of 'a' to 'z'. +0227 D132 D8 RET C +0228 D133 FE 7B CP '{' +0229 D135 D0 RET NC +0230 D136 E6 5F AND 5FH ;convert it if found. +0231 D138 C9 RET +0232 D139 ; +0233 D139 ; Routine to get a line of input. We must check to see if the +0234 D139 ; user is in (BATCH) mode. If so, then read the input from file +0235 D139 ; ($$$.SUB). At the end, reset to console input. +0236 D139 ; +0237 D139 3A AB D7 GETINP: LD A,(BATCH) ;if =0, then use console input. +0238 D13C B7 OR A +0239 D13D CA 96 D1 JP Z,GETINP1 +0240 D140 ; +0241 D140 ; Use the submit file ($$$.sub) which is prepared by a +0242 D140 ; SUBMIT run. It must be on drive (A) and it will be deleted +0243 D140 ; if and error occures (like eof). +0244 D140 ; +0245 D140 3A EF D7 LD A,(CDRIVE) ;select drive 0 if need be. +0246 D143 B7 OR A +0247 D144 3E 00 LD A,0 ;always use drive A for submit. +0248 D146 C4 BD D0 CALL NZ,DSKSEL ;select it if required. +0249 D149 11 AC D7 LD DE,BATCHFCB +0250 D14C CD CB D0 CALL OPEN ;look for it. +0251 D14F CA 96 D1 JP Z,GETINP1 ;if not there, use normal input. +0252 D152 3A BB D7 LD A,(BATCHFCB+15) ;get last record number+1. +0253 D155 3D DEC A +0254 D156 32 CC D7 LD (BATCHFCB+32),A +0255 D159 11 AC D7 LD DE,BATCHFCB +0256 D15C CD F9 D0 CALL RDREC ;read last record. +0257 D15F C2 96 D1 JP NZ,GETINP1 ;quit on end of file. +0258 D162 ; +0259 D162 ; Move this record into input buffer. +0260 D162 ; +0261 D162 11 07 D0 LD DE,INBUFF+1 +0262 D165 21 80 00 LD HL,TBUFF ;data was read into buffer here. +0263 D168 06 80 LD B,128 ;all 128 characters may be used. +0264 D16A CD 42 D4 CALL HL2DE ;(HL) to (DE), (B) bytes. +0265 D16D 21 BA D7 LD HL,BATCHFCB+14 +0266 D170 36 00 LD (HL),0 ;zero out the 's2' byte. +0267 D172 23 INC HL ;and decrement the record count. +0268 D173 35 DEC (HL) +0269 D174 11 AC D7 LD DE,BATCHFCB ;close the batch file now. +0270 D177 CD DA D0 CALL CLOSE +0271 D17A CA 96 D1 JP Z,GETINP1 ;quit on an error. +0272 D17D 3A EF D7 LD A,(CDRIVE) ;re-select previous drive if need be. +0273 D180 B7 OR A +0274 D181 C4 BD D0 CALL NZ,DSKSEL ;don't do needless selects. +0275 D184 ; +0276 D184 ; Print line just read on console. +0277 D184 ; +0278 D184 21 08 D0 LD HL,INBUFF+2 +0279 D187 CD AC D0 CALL PLINE2 +0280 D18A CD C2 D1 CALL CHKCON ;check console, quit on a key. +0281 D18D CA A7 D1 JP Z,GETINP2 ;jump if no key is pressed. +0282 D190 ; +0283 D190 ; Terminate the submit job on any keyboard input. Delete this +0284 D190 ; file such that it is not re-started and jump to normal keyboard +0285 D190 ; input section. +0286 D190 ; +0287 D190 CD DD D1 CALL DELBATCH ;delete the batch file. +0288 D193 C3 82 D3 JP CMMND1 ;and restart command input. +0289 D196 ; +0290 D196 ; Get here for normal keyboard input. Delete the submit file +0291 D196 ; incase there was one. +0292 D196 ; +0293 D196 CD DD D1 GETINP1:CALL DELBATCH ;delete file ($$$.sub). +0294 D199 CD 1A D1 CALL SETCDRV ;reset active disk. +0295 D19C 0E 0A LD C,10 ;get line from console device. +0296 D19E 11 06 D0 LD DE,INBUFF +0297 D1A1 CD 05 00 CALL ENTRY +0298 D1A4 CD 29 D1 CALL MOVECD ;reset current drive (again). +0299 D1A7 ; +0300 D1A7 ; Convert input line to upper case. +0301 D1A7 ; +0302 D1A7 21 07 D0 GETINP2:LD HL,INBUFF+1 +0303 D1AA 46 LD B,(HL) ;(B)=character counter. +0304 D1AB 23 GETINP3:INC HL +0305 D1AC 78 LD A,B ;end of the line? +0306 D1AD B7 OR A +0307 D1AE CA BA D1 JP Z,GETINP4 +0308 D1B1 7E LD A,(HL) ;convert to upper case. +0309 D1B2 CD 30 D1 CALL UPPER +0310 D1B5 77 LD (HL),A +0311 D1B6 05 DEC B ;adjust character count. +0312 D1B7 C3 AB D1 JP GETINP3 +0313 D1BA 77 GETINP4:LD (HL),A ;add trailing null. +0314 D1BB 21 08 D0 LD HL,INBUFF+2 +0315 D1BE 22 88 D0 LD (INPOINT),HL ;reset input line pointer. +0316 D1C1 C9 RET +0317 D1C2 ; +0318 D1C2 ; Routine to check the console for a key pressed. The zero +0319 D1C2 ; flag is set is none, else the character is returned in (A). +0320 D1C2 ; +0321 D1C2 0E 0B CHKCON: LD C,11 ;check console. +0322 D1C4 CD 05 00 CALL ENTRY +0323 D1C7 B7 OR A +0324 D1C8 C8 RET Z ;return if nothing. +0325 D1C9 0E 01 LD C,1 ;else get character. +0326 D1CB CD 05 00 CALL ENTRY +0327 D1CE B7 OR A ;clear zero flag and return. +0328 D1CF C9 RET +0329 D1D0 ; +0330 D1D0 ; Routine to get the currently active drive number. +0331 D1D0 ; +0332 D1D0 0E 19 GETDSK: LD C,25 +0333 D1D2 C3 05 00 JP ENTRY +0334 D1D5 ; +0335 D1D5 ; Set the stabdard dma address. +0336 D1D5 ; +0337 D1D5 11 80 00 STDDMA: LD DE,TBUFF +0338 D1D8 ; +0339 D1D8 ; Routine to set the dma address to (DE). +0340 D1D8 ; +0341 D1D8 0E 1A DMASET: LD C,26 +0342 D1DA C3 05 00 JP ENTRY +0343 D1DD ; +0344 D1DD ; Delete the batch file created by SUBMIT. +0345 D1DD ; +0346 D1DD 21 AB D7 DELBATCH: LD HL,BATCH ;is batch active? +0347 D1E0 7E LD A,(HL) +0348 D1E1 B7 OR A +0349 D1E2 C8 RET Z +0350 D1E3 36 00 LD (HL),0 ;yes, de-activate it. +0351 D1E5 AF XOR A +0352 D1E6 CD BD D0 CALL DSKSEL ;select drive 0 for sure. +0353 D1E9 11 AC D7 LD DE,BATCHFCB ;and delete this file. +0354 D1EC CD EF D0 CALL DELETE +0355 D1EF 3A EF D7 LD A,(CDRIVE) ;reset current drive. +0356 D1F2 C3 BD D0 JP DSKSEL +0357 D1F5 ; +0358 D1F5 ; Check to two strings at (PATTRN1) and (PATTRN2). They must be +0359 D1F5 ; the same or we halt.... +0360 D1F5 ; +0361 D1F5 11 28 D3 VERIFY: LD DE,PATTRN1 ;these are the serial number bytes. +0362 D1F8 21 00 D8 LD HL,PATTRN2 ;ditto, but how could they be different? +0363 D1FB 06 06 LD B,6 ;6 bytes each. +0364 D1FD 1A VERIFY1:LD A,(DE) +0365 D1FE BE CP (HL) +0366 D1FF C2 CF D3 JP NZ,HALT ;jump to halt routine. +0367 D202 13 INC DE +0368 D203 23 INC HL +0369 D204 05 DEC B +0370 D205 C2 FD D1 JP NZ,VERIFY1 +0371 D208 C9 RET +0372 D209 ; +0373 D209 ; Print back file name with a '?' to indicate a syntax error. +0374 D209 ; +0375 D209 CD 98 D0 SYNERR: CALL CRLF ;end current line. +0376 D20C 2A 8A D0 LD HL,(NAMEPNT) ;this points to name in error. +0377 D20F 7E SYNERR1:LD A,(HL) ;print it until a space or null is found. +0378 D210 FE 20 CP ' ' +0379 D212 CA 22 D2 JP Z,SYNERR2 +0380 D215 B7 OR A +0381 D216 CA 22 D2 JP Z,SYNERR2 +0382 D219 E5 PUSH HL +0383 D21A CD 8C D0 CALL PRINT +0384 D21D E1 POP HL +0385 D21E 23 INC HL +0386 D21F C3 0F D2 JP SYNERR1 +0387 D222 3E 3F SYNERR2:LD A,'?' ;add trailing '?'. +0388 D224 CD 8C D0 CALL PRINT +0389 D227 CD 98 D0 CALL CRLF +0390 D22A CD DD D1 CALL DELBATCH ;delete any batch file. +0391 D22D C3 82 D3 JP CMMND1 ;and restart from console input. +0392 D230 ; +0393 D230 ; Check character at (DE) for legal command input. Note that the +0394 D230 ; zero flag is set if the character is a delimiter. +0395 D230 ; +0396 D230 1A CHECK: LD A,(DE) +0397 D231 B7 OR A +0398 D232 C8 RET Z +0399 D233 FE 20 CP ' ' ;control characters are not legal here. +0400 D235 DA 09 D2 JP C,SYNERR +0401 D238 C8 RET Z ;check for valid delimiter. +0402 D239 FE 3D CP '=' +0403 D23B C8 RET Z +0404 D23C FE 5F CP '_' +0405 D23E C8 RET Z +0406 D23F FE 2E CP '.' +0407 D241 C8 RET Z +0408 D242 FE 3A CP ':' +0409 D244 C8 RET Z +0410 D245 FE 3B CP 03BH ; ';' +0411 D247 C8 RET Z +0412 D248 FE 3C CP '<' +0413 D24A C8 RET Z +0414 D24B FE 3E CP '>' +0415 D24D C8 RET Z +0416 D24E C9 RET +0417 D24F ; +0418 D24F ; Get the next non-blank character from (DE). +0419 D24F ; +0420 D24F 1A NONBLANK: LD A,(DE) +0421 D250 B7 OR A ;string ends with a null. +0422 D251 C8 RET Z +0423 D252 FE 20 CP ' ' +0424 D254 C0 RET NZ +0425 D255 13 INC DE +0426 D256 C3 4F D2 JP NONBLANK +0427 D259 ; +0428 D259 ; Add (HL)=(HL)+(A) +0429 D259 ; +0430 D259 85 ADDHL: ADD A,L +0431 D25A 6F LD L,A +0432 D25B D0 RET NC ;take care of any carry. +0433 D25C 24 INC H +0434 D25D C9 RET +0435 D25E ; +0436 D25E ; Convert the first name in (FCB). +0437 D25E ; +0438 D25E 3E 00 CONVFST:LD A,0 +0439 D260 ; +0440 D260 ; Format a file name (convert * to '?', etc.). On return, +0441 D260 ; (A)=0 is an unambigeous name was specified. Enter with (A) equal to +0442 D260 ; the position within the fcb for the name (either 0 or 16). +0443 D260 ; +0444 D260 21 CD D7 CONVERT:LD HL,FCB +0445 D263 CD 59 D2 CALL ADDHL +0446 D266 E5 PUSH HL +0447 D267 E5 PUSH HL +0448 D268 AF XOR A +0449 D269 32 F0 D7 LD (CHGDRV),A ;initialize drive change flag. +0450 D26C 2A 88 D0 LD HL,(INPOINT) ;set (HL) as pointer into input line. +0451 D26F EB EX DE,HL +0452 D270 CD 4F D2 CALL NONBLANK ;get next non-blank character. +0453 D273 EB EX DE,HL +0454 D274 22 8A D0 LD (NAMEPNT),HL ;save pointer here for any error message. +0455 D277 EB EX DE,HL +0456 D278 E1 POP HL +0457 D279 1A LD A,(DE) ;get first character. +0458 D27A B7 OR A +0459 D27B CA 89 D2 JP Z,CONVRT1 +0460 D27E DE 40 SBC A,'A'-1 ;might be a drive name, convert to binary. +0461 D280 47 LD B,A ;and save. +0462 D281 13 INC DE ;check next character for a ':'. +0463 D282 1A LD A,(DE) +0464 D283 FE 3A CP ':' +0465 D285 CA 90 D2 JP Z,CONVRT2 +0466 D288 1B DEC DE ;nope, move pointer back to the start of the line. +0467 D289 3A EF D7 CONVRT1:LD A,(CDRIVE) +0468 D28C 77 LD (HL),A +0469 D28D C3 96 D2 JP CONVRT3 +0470 D290 78 CONVRT2:LD A,B +0471 D291 32 F0 D7 LD (CHGDRV),A ;set change in drives flag. +0472 D294 70 LD (HL),B +0473 D295 13 INC DE +0474 D296 ; +0475 D296 ; Convert the basic file name. +0476 D296 ; +0477 D296 06 08 CONVRT3:LD B,08H +0478 D298 CD 30 D2 CONVRT4:CALL CHECK +0479 D29B CA B9 D2 JP Z,CONVRT8 +0480 D29E 23 INC HL +0481 D29F FE 2A CP '*' ;note that an '*' will fill the remaining +0482 D2A1 C2 A9 D2 JP NZ,CONVRT5 ;field with '?'. +0483 D2A4 36 3F LD (HL),'?' +0484 D2A6 C3 AB D2 JP CONVRT6 +0485 D2A9 77 CONVRT5:LD (HL),A +0486 D2AA 13 INC DE +0487 D2AB 05 CONVRT6:DEC B +0488 D2AC C2 98 D2 JP NZ,CONVRT4 +0489 D2AF CD 30 D2 CONVRT7:CALL CHECK ;get next delimiter. +0490 D2B2 CA C0 D2 JP Z,GETEXT +0491 D2B5 13 INC DE +0492 D2B6 C3 AF D2 JP CONVRT7 +0493 D2B9 23 CONVRT8:INC HL ;blank fill the file name. +0494 D2BA 36 20 LD (HL),' ' +0495 D2BC 05 DEC B +0496 D2BD C2 B9 D2 JP NZ,CONVRT8 +0497 D2C0 ; +0498 D2C0 ; Get the extension and convert it. +0499 D2C0 ; +0500 D2C0 06 03 GETEXT: LD B,03H +0501 D2C2 FE 2E CP '.' +0502 D2C4 C2 E9 D2 JP NZ,GETEXT5 +0503 D2C7 13 INC DE +0504 D2C8 CD 30 D2 GETEXT1:CALL CHECK +0505 D2CB CA E9 D2 JP Z,GETEXT5 +0506 D2CE 23 INC HL +0507 D2CF FE 2A CP '*' +0508 D2D1 C2 D9 D2 JP NZ,GETEXT2 +0509 D2D4 36 3F LD (HL),'?' +0510 D2D6 C3 DB D2 JP GETEXT3 +0511 D2D9 77 GETEXT2:LD (HL),A +0512 D2DA 13 INC DE +0513 D2DB 05 GETEXT3:DEC B +0514 D2DC C2 C8 D2 JP NZ,GETEXT1 +0515 D2DF CD 30 D2 GETEXT4:CALL CHECK +0516 D2E2 CA F0 D2 JP Z,GETEXT6 +0517 D2E5 13 INC DE +0518 D2E6 C3 DF D2 JP GETEXT4 +0519 D2E9 23 GETEXT5:INC HL +0520 D2EA 36 20 LD (HL),' ' +0521 D2EC 05 DEC B +0522 D2ED C2 E9 D2 JP NZ,GETEXT5 +0523 D2F0 06 03 GETEXT6:LD B,3 +0524 D2F2 23 GETEXT7:INC HL +0525 D2F3 36 00 LD (HL),0 +0526 D2F5 05 DEC B +0527 D2F6 C2 F2 D2 JP NZ,GETEXT7 +0528 D2F9 EB EX DE,HL +0529 D2FA 22 88 D0 LD (INPOINT),HL ;save input line pointer. +0530 D2FD E1 POP HL +0531 D2FE ; +0532 D2FE ; Check to see if this is an ambigeous file name specification. +0533 D2FE ; Set the (A) register to non zero if it is. +0534 D2FE ; +0535 D2FE 01 0B 00 LD BC,11 ;set name length. +0536 D301 23 GETEXT8:INC HL +0537 D302 7E LD A,(HL) +0538 D303 FE 3F CP '?' ;any question marks? +0539 D305 C2 09 D3 JP NZ,GETEXT9 +0540 D308 04 INC B ;count them. +0541 D309 0D GETEXT9:DEC C +0542 D30A C2 01 D3 JP NZ,GETEXT8 +0543 D30D 78 LD A,B +0544 D30E B7 OR A +0545 D30F C9 RET +0546 D310 ; +0547 D310 ; CP/M command table. Note commands can be either 3 or 4 characters long. +0548 D310 ; +0549 D310 NUMCMDS .EQU 6 ;number of commands +0550 D310 44 49 52 20 CMDTBL: .TEXT "DIR " +0551 D314 45 52 41 20 .TEXT "ERA " +0552 D318 54 59 50 45 .TEXT "TYPE" +0553 D31C 53 41 56 45 .TEXT "SAVE" +0554 D320 52 45 4E 20 .TEXT "REN " +0555 D324 55 53 45 52 .TEXT "USER" +0556 D328 ; +0557 D328 ; The following six bytes must agree with those at (PATTRN2) +0558 D328 ; or cp/m will HALT. Why? +0559 D328 ; +0560 D328 001600000000PATTRN1:.DB 0,22,0,0,0,0 ;(* serial number bytes *). +0561 D32E ; +0562 D32E ; Search the command table for a match with what has just +0563 D32E ; been entered. If a match is found, then we jump to the +0564 D32E ; proper section. Else jump to (UNKNOWN). +0565 D32E ; On return, the (C) register is set to the command number +0566 D32E ; that matched (or NUMCMDS+1 if no match). +0567 D32E ; +0568 D32E 21 10 D3 SEARCH: LD HL,CMDTBL +0569 D331 0E 00 LD C,0 +0570 D333 79 SEARCH1:LD A,C +0571 D334 FE 06 CP NUMCMDS ;this commands exists. +0572 D336 D0 RET NC +0573 D337 11 CE D7 LD DE,FCB+1 ;check this one. +0574 D33A 06 04 LD B,4 ;max command length. +0575 D33C 1A SEARCH2:LD A,(DE) +0576 D33D BE CP (HL) +0577 D33E C2 4F D3 JP NZ,SEARCH3 ;not a match. +0578 D341 13 INC DE +0579 D342 23 INC HL +0580 D343 05 DEC B +0581 D344 C2 3C D3 JP NZ,SEARCH2 +0582 D347 1A LD A,(DE) ;allow a 3 character command to match. +0583 D348 FE 20 CP ' ' +0584 D34A C2 54 D3 JP NZ,SEARCH4 +0585 D34D 79 LD A,C ;set return register for this command. +0586 D34E C9 RET +0587 D34F 23 SEARCH3:INC HL +0588 D350 05 DEC B +0589 D351 C2 4F D3 JP NZ,SEARCH3 +0590 D354 0C SEARCH4:INC C +0591 D355 C3 33 D3 JP SEARCH1 +0592 D358 ; +0593 D358 ; Set the input buffer to empty and then start the command +0594 D358 ; processor (ccp). +0595 D358 ; +0596 D358 AF CLEARBUF: XOR A +0597 D359 32 07 D0 LD (INBUFF+1),A ;second byte is actual length. +0598 D35C ; +0599 D35C ;************************************************************** +0600 D35C ;* +0601 D35C ;* +0602 D35C ;* C C P - C o n s o l e C o m m a n d P r o c e s s o r +0603 D35C ;* +0604 D35C ;************************************************************** +0605 D35C ;* +0606 D35C 31 AB D7 COMMAND:LD SP,CCPSTACK ;setup stack area. +0607 D35F C5 PUSH BC ;note that (C) should be equal to: +0608 D360 79 LD A,C ;(uuuudddd) where 'uuuu' is the user number +0609 D361 1F RRA ;and 'dddd' is the drive number. +0610 D362 1F RRA +0611 D363 1F RRA +0612 D364 1F RRA +0613 D365 E6 0F AND 0FH ;isolate the user number. +0614 D367 5F LD E,A +0615 D368 CD 15 D1 CALL GETSETUC ;and set it. +0616 D36B CD B8 D0 CALL RESDSK ;reset the disk system. +0617 D36E 32 AB D7 LD (BATCH),A ;clear batch mode flag. +0618 D371 C1 POP BC +0619 D372 79 LD A,C +0620 D373 E6 0F AND 0FH ;isolate the drive number. +0621 D375 32 EF D7 LD (CDRIVE),A ;and save. +0622 D378 CD BD D0 CALL DSKSEL ;...and select. +0623 D37B 3A 07 D0 LD A,(INBUFF+1) +0624 D37E B7 OR A ;anything in input buffer already? +0625 D37F C2 98 D3 JP NZ,CMMND2 ;yes, we just process it. +0626 D382 ; +0627 D382 ; Entry point to get a command line from the console. +0628 D382 ; +0629 D382 31 AB D7 CMMND1: LD SP,CCPSTACK ;set stack straight. +0630 D385 CD 98 D0 CALL CRLF ;start a new line on the screen. +0631 D388 CD D0 D1 CALL GETDSK ;get current drive. +0632 D38B C6 41 ADD A,'A' +0633 D38D CD 8C D0 CALL PRINT ;print current drive. +0634 D390 3E 3E LD A,'>' +0635 D392 CD 8C D0 CALL PRINT ;and add prompt. +0636 D395 CD 39 D1 CALL GETINP ;get line from user. +0637 D398 ; +0638 D398 ; Process command line here. +0639 D398 ; +0640 D398 11 80 00 CMMND2: LD DE,TBUFF +0641 D39B CD D8 D1 CALL DMASET ;set standard dma address. +0642 D39E CD D0 D1 CALL GETDSK +0643 D3A1 32 EF D7 LD (CDRIVE),A ;set current drive. +0644 D3A4 CD 5E D2 CALL CONVFST ;convert name typed in. +0645 D3A7 C4 09 D2 CALL NZ,SYNERR ;wild cards are not allowed. +0646 D3AA 3A F0 D7 LD A,(CHGDRV) ;if a change in drives was indicated, +0647 D3AD B7 OR A ;then treat this as an unknown command +0648 D3AE C2 A5 D6 JP NZ,UNKNOWN ;which gets executed. +0649 D3B1 CD 2E D3 CALL SEARCH ;else search command table for a match. +0650 D3B4 ; +0651 D3B4 ; Note that an unknown command returns +0652 D3B4 ; with (A) pointing to the last address +0653 D3B4 ; in our table which is (UNKNOWN). +0654 D3B4 ; +0655 D3B4 21 C1 D3 LD HL,CMDADR ;now, look thru our address table for command (A). +0656 D3B7 5F LD E,A ;set (DE) to command number. +0657 D3B8 16 00 LD D,0 +0658 D3BA 19 ADD HL,DE +0659 D3BB 19 ADD HL,DE ;(HL)=(CMDADR)+2*(command number). +0660 D3BC 7E LD A,(HL) ;now pick out this address. +0661 D3BD 23 INC HL +0662 D3BE 66 LD H,(HL) +0663 D3BF 6F LD L,A +0664 D3C0 E9 JP (HL) ;now execute it. +0665 D3C1 ; +0666 D3C1 ; CP/M command address table. +0667 D3C1 ; +0668 D3C1 77D41FD55DD5CMDADR: .DW DIRECT,ERASE,TYPE,SAVE +0668 D3C7 ADD5 +0669 D3C9 10D68ED6A5D6 .DW RENAME,USER,UNKNOWN +0670 D3CF ; +0671 D3CF ; Halt the system. Reason for this is unknown at present. +0672 D3CF ; +0673 D3CF 21 F3 76 HALT: LD HL,76F3H ;'DI HLT' instructions. +0674 D3D2 22 00 D0 LD (CBASE),HL +0675 D3D5 21 00 D0 LD HL,CBASE +0676 D3D8 E9 JP (HL) +0677 D3D9 ; +0678 D3D9 ; Read error while TYPEing a file. +0679 D3D9 ; +0680 D3D9 01 DF D3 RDERROR:LD BC,RDERR +0681 D3DC C3 A7 D0 JP PLINE +0682 D3DF 526561642065RDERR: .TEXT "Read error" +0682 D3E5 72726F72 +0683 D3E9 00 .DB 0 +0684 D3EA ; +0685 D3EA ; Required file was not located. +0686 D3EA ; +0687 D3EA 01 F0 D3 NONE: LD BC,NOFILE +0688 D3ED C3 A7 D0 JP PLINE +0689 D3F0 4E6F2066696CNOFILE: .TEXT "No file" +0689 D3F6 65 +0690 D3F7 00 .DB 0 +0691 D3F8 ; +0692 D3F8 ; Decode a command of the form 'A>filename number{ filename}. +0693 D3F8 ; Note that a drive specifier is not allowed on the first file +0694 D3F8 ; name. On return, the number is in register (A). Any error +0695 D3F8 ; causes 'filename?' to be printed and the command is aborted. +0696 D3F8 ; +0697 D3F8 CD 5E D2 DECODE: CALL CONVFST ;convert filename. +0698 D3FB 3A F0 D7 LD A,(CHGDRV) ;do not allow a drive to be specified. +0699 D3FE B7 OR A +0700 D3FF C2 09 D2 JP NZ,SYNERR +0701 D402 21 CE D7 LD HL,FCB+1 ;convert number now. +0702 D405 01 0B 00 LD BC,11 ;(B)=sum register, (C)=max digit count. +0703 D408 7E DECODE1:LD A,(HL) +0704 D409 FE 20 CP ' ' ;a space terminates the numeral. +0705 D40B CA 33 D4 JP Z,DECODE3 +0706 D40E 23 INC HL +0707 D40F D6 30 SUB '0' ;make binary from ascii. +0708 D411 FE 0A CP 10 ;legal digit? +0709 D413 D2 09 D2 JP NC,SYNERR +0710 D416 57 LD D,A ;yes, save it in (D). +0711 D417 78 LD A,B ;compute (B)=(B)*10 and check for overflow. +0712 D418 E6 E0 AND 0E0H +0713 D41A C2 09 D2 JP NZ,SYNERR +0714 D41D 78 LD A,B +0715 D41E 07 RLCA +0716 D41F 07 RLCA +0717 D420 07 RLCA ;(A)=(B)*8 +0718 D421 80 ADD A,B ;.......*9 +0719 D422 DA 09 D2 JP C,SYNERR +0720 D425 80 ADD A,B ;.......*10 +0721 D426 DA 09 D2 JP C,SYNERR +0722 D429 82 ADD A,D ;add in new digit now. +0723 D42A DA 09 D2 DECODE2:JP C,SYNERR +0724 D42D 47 LD B,A ;and save result. +0725 D42E 0D DEC C ;only look at 11 digits. +0726 D42F C2 08 D4 JP NZ,DECODE1 +0727 D432 C9 RET +0728 D433 7E DECODE3:LD A,(HL) ;spaces must follow (why?). +0729 D434 FE 20 CP ' ' +0730 D436 C2 09 D2 JP NZ,SYNERR +0731 D439 23 INC HL +0732 D43A 0D DECODE4:DEC C +0733 D43B C2 33 D4 JP NZ,DECODE3 +0734 D43E 78 LD A,B ;set (A)=the numeric value entered. +0735 D43F C9 RET +0736 D440 ; +0737 D440 ; Move 3 bytes from (HL) to (DE). Note that there is only +0738 D440 ; one reference to this at (A2D5h). +0739 D440 ; +0740 D440 06 03 MOVE3: LD B,3 +0741 D442 ; +0742 D442 ; Move (B) bytes from (HL) to (DE). +0743 D442 ; +0744 D442 7E HL2DE: LD A,(HL) +0745 D443 12 LD (DE),A +0746 D444 23 INC HL +0747 D445 13 INC DE +0748 D446 05 DEC B +0749 D447 C2 42 D4 JP NZ,HL2DE +0750 D44A C9 RET +0751 D44B ; +0752 D44B ; Compute (HL)=(TBUFF)+(A)+(C) and get the byte that's here. +0753 D44B ; +0754 D44B 21 80 00 EXTRACT:LD HL,TBUFF +0755 D44E 81 ADD A,C +0756 D44F CD 59 D2 CALL ADDHL +0757 D452 7E LD A,(HL) +0758 D453 C9 RET +0759 D454 ; +0760 D454 ; Check drive specified. If it means a change, then the new +0761 D454 ; drive will be selected. In any case, the drive byte of the +0762 D454 ; fcb will be set to null (means use current drive). +0763 D454 ; +0764 D454 AF DSELECT:XOR A ;null out first byte of fcb. +0765 D455 32 CD D7 LD (FCB),A +0766 D458 3A F0 D7 LD A,(CHGDRV) ;a drive change indicated? +0767 D45B B7 OR A +0768 D45C C8 RET Z +0769 D45D 3D DEC A ;yes, is it the same as the current drive? +0770 D45E 21 EF D7 LD HL,CDRIVE +0771 D461 BE CP (HL) +0772 D462 C8 RET Z +0773 D463 C3 BD D0 JP DSKSEL ;no. Select it then. +0774 D466 ; +0775 D466 ; Check the drive selection and reset it to the previous +0776 D466 ; drive if it was changed for the preceeding command. +0777 D466 ; +0778 D466 3A F0 D7 RESETDR:LD A,(CHGDRV) ;drive change indicated? +0779 D469 B7 OR A +0780 D46A C8 RET Z +0781 D46B 3D DEC A ;yes, was it a different drive? +0782 D46C 21 EF D7 LD HL,CDRIVE +0783 D46F BE CP (HL) +0784 D470 C8 RET Z +0785 D471 3A EF D7 LD A,(CDRIVE) ;yes, re-select our old drive. +0786 D474 C3 BD D0 JP DSKSEL +0787 D477 ; +0788 D477 ;************************************************************** +0789 D477 ;* +0790 D477 ;* D I R E C T O R Y C O M M A N D +0791 D477 ;* +0792 D477 ;************************************************************** +0793 D477 ; +0794 D477 CD 5E D2 DIRECT: CALL CONVFST ;convert file name. +0795 D47A CD 54 D4 CALL DSELECT ;select indicated drive. +0796 D47D 21 CE D7 LD HL,FCB+1 ;was any file indicated? +0797 D480 7E LD A,(HL) +0798 D481 FE 20 CP ' ' +0799 D483 C2 8F D4 JP NZ,DIRECT2 +0800 D486 06 0B LD B,11 ;no. Fill field with '?' - same as *.*. +0801 D488 36 3F DIRECT1:LD (HL),'?' +0802 D48A 23 INC HL +0803 D48B 05 DEC B +0804 D48C C2 88 D4 JP NZ,DIRECT1 +0805 D48F 1E 00 DIRECT2:LD E,0 ;set initial cursor position. +0806 D491 D5 PUSH DE +0807 D492 CD E9 D0 CALL SRCHFCB ;get first file name. +0808 D495 CC EA D3 CALL Z,NONE ;none found at all? +0809 D498 CA 1B D5 DIRECT3:JP Z,DIRECT9 ;terminate if no more names. +0810 D49B 3A EE D7 LD A,(RTNCODE) ;get file's position in segment (0-3). +0811 D49E 0F RRCA +0812 D49F 0F RRCA +0813 D4A0 0F RRCA +0814 D4A1 E6 60 AND 60H ;(A)=position*32 +0815 D4A3 4F LD C,A +0816 D4A4 3E 0A LD A,10 +0817 D4A6 CD 4B D4 CALL EXTRACT ;extract the tenth entry in fcb. +0818 D4A9 17 RLA ;check system file status bit. +0819 D4AA DA 0F D5 JP C,DIRECT8 ;we don't list them. +0820 D4AD D1 POP DE +0821 D4AE 7B LD A,E ;bump name count. +0822 D4AF 1C INC E +0823 D4B0 D5 PUSH DE +0824 D4B1 E6 03 AND 03H ;at end of line? +0825 D4B3 F5 PUSH AF +0826 D4B4 C2 CC D4 JP NZ,DIRECT4 +0827 D4B7 CD 98 D0 CALL CRLF ;yes, end this line and start another. +0828 D4BA C5 PUSH BC +0829 D4BB CD D0 D1 CALL GETDSK ;start line with ('A:'). +0830 D4BE C1 POP BC +0831 D4BF C6 41 ADD A,'A' +0832 D4C1 CD 92 D0 CALL PRINTB +0833 D4C4 3E 3A LD A,':' +0834 D4C6 CD 92 D0 CALL PRINTB +0835 D4C9 C3 D4 D4 JP DIRECT5 +0836 D4CC CD A2 D0 DIRECT4:CALL SPACE ;add seperator between file names. +0837 D4CF 3E 3A LD A,':' +0838 D4D1 CD 92 D0 CALL PRINTB +0839 D4D4 CD A2 D0 DIRECT5:CALL SPACE +0840 D4D7 06 01 LD B,1 ;'extract' each file name character at a time. +0841 D4D9 78 DIRECT6:LD A,B +0842 D4DA CD 4B D4 CALL EXTRACT +0843 D4DD E6 7F AND 7FH ;strip bit 7 (status bit). +0844 D4DF FE 20 CP ' ' ;are we at the end of the name? +0845 D4E1 C2 F9 D4 JP NZ,DRECT65 +0846 D4E4 F1 POP AF ;yes, don't print spaces at the end of a line. +0847 D4E5 F5 PUSH AF +0848 D4E6 FE 03 CP 3 +0849 D4E8 C2 F7 D4 JP NZ,DRECT63 +0850 D4EB 3E 09 LD A,9 ;first check for no extension. +0851 D4ED CD 4B D4 CALL EXTRACT +0852 D4F0 E6 7F AND 7FH +0853 D4F2 FE 20 CP ' ' +0854 D4F4 CA 0E D5 JP Z,DIRECT7 ;don't print spaces. +0855 D4F7 3E 20 DRECT63:LD A,' ' ;else print them. +0856 D4F9 CD 92 D0 DRECT65:CALL PRINTB +0857 D4FC 04 INC B ;bump to next character psoition. +0858 D4FD 78 LD A,B +0859 D4FE FE 0C CP 12 ;end of the name? +0860 D500 D2 0E D5 JP NC,DIRECT7 +0861 D503 FE 09 CP 9 ;nope, starting extension? +0862 D505 C2 D9 D4 JP NZ,DIRECT6 +0863 D508 CD A2 D0 CALL SPACE ;yes, add seperating space. +0864 D50B C3 D9 D4 JP DIRECT6 +0865 D50E F1 DIRECT7:POP AF ;get the next file name. +0866 D50F CD C2 D1 DIRECT8:CALL CHKCON ;first check console, quit on anything. +0867 D512 C2 1B D5 JP NZ,DIRECT9 +0868 D515 CD E4 D0 CALL SRCHNXT ;get next name. +0869 D518 C3 98 D4 JP DIRECT3 ;and continue with our list. +0870 D51B D1 DIRECT9:POP DE ;restore the stack and return to command level. +0871 D51C C3 86 D7 JP GETBACK +0872 D51F ; +0873 D51F ;************************************************************** +0874 D51F ;* +0875 D51F ;* E R A S E C O M M A N D +0876 D51F ;* +0877 D51F ;************************************************************** +0878 D51F ; +0879 D51F CD 5E D2 ERASE: CALL CONVFST ;convert file name. +0880 D522 FE 0B CP 11 ;was '*.*' entered? +0881 D524 C2 42 D5 JP NZ,ERASE1 +0882 D527 01 52 D5 LD BC,YESNO ;yes, ask for confirmation. +0883 D52A CD A7 D0 CALL PLINE +0884 D52D CD 39 D1 CALL GETINP +0885 D530 21 07 D0 LD HL,INBUFF+1 +0886 D533 35 DEC (HL) ;must be exactly 'y'. +0887 D534 C2 82 D3 JP NZ,CMMND1 +0888 D537 23 INC HL +0889 D538 7E LD A,(HL) +0890 D539 FE 59 CP 'Y' +0891 D53B C2 82 D3 JP NZ,CMMND1 +0892 D53E 23 INC HL +0893 D53F 22 88 D0 LD (INPOINT),HL ;save input line pointer. +0894 D542 CD 54 D4 ERASE1: CALL DSELECT ;select desired disk. +0895 D545 11 CD D7 LD DE,FCB +0896 D548 CD EF D0 CALL DELETE ;delete the file. +0897 D54B 3C INC A +0898 D54C CC EA D3 CALL Z,NONE ;not there? +0899 D54F C3 86 D7 JP GETBACK ;return to command level now. +0900 D552 416C6C202879YESNO: .TEXT "All (y/n)?" +0900 D558 2F6E293F +0901 D55C 00 .DB 0 +0902 D55D ; +0903 D55D ;************************************************************** +0904 D55D ;* +0905 D55D ;* T Y P E C O M M A N D +0906 D55D ;* +0907 D55D ;************************************************************** +0908 D55D ; +0909 D55D CD 5E D2 TYPE: CALL CONVFST ;convert file name. +0910 D560 C2 09 D2 JP NZ,SYNERR ;wild cards not allowed. +0911 D563 CD 54 D4 CALL DSELECT ;select indicated drive. +0912 D566 CD D0 D0 CALL OPENFCB ;open the file. +0913 D569 CA A7 D5 JP Z,TYPE5 ;not there? +0914 D56C CD 98 D0 CALL CRLF ;ok, start a new line on the screen. +0915 D56F 21 F1 D7 LD HL,NBYTES ;initialize byte counter. +0916 D572 36 FF LD (HL),0FFH ;set to read first sector. +0917 D574 21 F1 D7 TYPE1: LD HL,NBYTES +0918 D577 7E TYPE2: LD A,(HL) ;have we written the entire sector? +0919 D578 FE 80 CP 128 +0920 D57A DA 87 D5 JP C,TYPE3 +0921 D57D E5 PUSH HL ;yes, read in the next one. +0922 D57E CD FE D0 CALL READFCB +0923 D581 E1 POP HL +0924 D582 C2 A0 D5 JP NZ,TYPE4 ;end or error? +0925 D585 AF XOR A ;ok, clear byte counter. +0926 D586 77 LD (HL),A +0927 D587 34 TYPE3: INC (HL) ;count this byte. +0928 D588 21 80 00 LD HL,TBUFF ;and get the (A)th one from the buffer (TBUFF). +0929 D58B CD 59 D2 CALL ADDHL +0930 D58E 7E LD A,(HL) +0931 D58F FE 1A CP CNTRLZ ;end of file mark? +0932 D591 CA 86 D7 JP Z,GETBACK +0933 D594 CD 8C D0 CALL PRINT ;no, print it. +0934 D597 CD C2 D1 CALL CHKCON ;check console, quit if anything ready. +0935 D59A C2 86 D7 JP NZ,GETBACK +0936 D59D C3 74 D5 JP TYPE1 +0937 D5A0 ; +0938 D5A0 ; Get here on an end of file or read error. +0939 D5A0 ; +0940 D5A0 3D TYPE4: DEC A ;read error? +0941 D5A1 CA 86 D7 JP Z,GETBACK +0942 D5A4 CD D9 D3 CALL RDERROR ;yes, print message. +0943 D5A7 CD 66 D4 TYPE5: CALL RESETDR ;and reset proper drive +0944 D5AA C3 09 D2 JP SYNERR ;now print file name with problem. +0945 D5AD ; +0946 D5AD ;************************************************************** +0947 D5AD ;* +0948 D5AD ;* S A V E C O M M A N D +0949 D5AD ;* +0950 D5AD ;************************************************************** +0951 D5AD ; +0952 D5AD CD F8 D3 SAVE: CALL DECODE ;get numeric number that follows SAVE. +0953 D5B0 F5 PUSH AF ;save number of pages to write. +0954 D5B1 CD 5E D2 CALL CONVFST ;convert file name. +0955 D5B4 C2 09 D2 JP NZ,SYNERR ;wild cards not allowed. +0956 D5B7 CD 54 D4 CALL DSELECT ;select specified drive. +0957 D5BA 11 CD D7 LD DE,FCB ;now delete this file. +0958 D5BD D5 PUSH DE +0959 D5BE CD EF D0 CALL DELETE +0960 D5C1 D1 POP DE +0961 D5C2 CD 09 D1 CALL CREATE ;and create it again. +0962 D5C5 CA FB D5 JP Z,SAVE3 ;can't create? +0963 D5C8 AF XOR A ;clear record number byte. +0964 D5C9 32 ED D7 LD (FCB+32),A +0965 D5CC F1 POP AF ;convert pages to sectors. +0966 D5CD 6F LD L,A +0967 D5CE 26 00 LD H,0 +0968 D5D0 29 ADD HL,HL ;(HL)=number of sectors to write. +0969 D5D1 11 00 01 LD DE,TBASE ;and we start from here. +0970 D5D4 7C SAVE1: LD A,H ;done yet? +0971 D5D5 B5 OR L +0972 D5D6 CA F1 D5 JP Z,SAVE2 +0973 D5D9 2B DEC HL ;nope, count this and compute the start +0974 D5DA E5 PUSH HL ;of the next 128 byte sector. +0975 D5DB 21 80 00 LD HL,128 +0976 D5DE 19 ADD HL,DE +0977 D5DF E5 PUSH HL ;save it and set the transfer address. +0978 D5E0 CD D8 D1 CALL DMASET +0979 D5E3 11 CD D7 LD DE,FCB ;write out this sector now. +0980 D5E6 CD 04 D1 CALL WRTREC +0981 D5E9 D1 POP DE ;reset (DE) to the start of the last sector. +0982 D5EA E1 POP HL ;restore sector count. +0983 D5EB C2 FB D5 JP NZ,SAVE3 ;write error? +0984 D5EE C3 D4 D5 JP SAVE1 +0985 D5F1 ; +0986 D5F1 ; Get here after writing all of the file. +0987 D5F1 ; +0988 D5F1 11 CD D7 SAVE2: LD DE,FCB ;now close the file. +0989 D5F4 CD DA D0 CALL CLOSE +0990 D5F7 3C INC A ;did it close ok? +0991 D5F8 C2 01 D6 JP NZ,SAVE4 +0992 D5FB ; +0993 D5FB ; Print out error message (no space). +0994 D5FB ; +0995 D5FB 01 07 D6 SAVE3: LD BC,NOSPACE +0996 D5FE CD A7 D0 CALL PLINE +0997 D601 CD D5 D1 SAVE4: CALL STDDMA ;reset the standard dma address. +0998 D604 C3 86 D7 JP GETBACK +0999 D607 4E6F20737061NOSPACE:.TEXT "No space" +0999 D60D 6365 +1000 D60F 00 .DB 0 +1001 D610 ; +1002 D610 ;************************************************************** +1003 D610 ;* +1004 D610 ;* R E N A M E C O M M A N D +1005 D610 ;* +1006 D610 ;************************************************************** +1007 D610 ; +1008 D610 CD 5E D2 RENAME: CALL CONVFST ;convert first file name. +1009 D613 C2 09 D2 JP NZ,SYNERR ;wild cards not allowed. +1010 D616 3A F0 D7 LD A,(CHGDRV) ;remember any change in drives specified. +1011 D619 F5 PUSH AF +1012 D61A CD 54 D4 CALL DSELECT ;and select this drive. +1013 D61D CD E9 D0 CALL SRCHFCB ;is this file present? +1014 D620 C2 79 D6 JP NZ,RENAME6 ;yes, print error message. +1015 D623 21 CD D7 LD HL,FCB ;yes, move this name into second slot. +1016 D626 11 DD D7 LD DE,FCB+16 +1017 D629 06 10 LD B,16 +1018 D62B CD 42 D4 CALL HL2DE +1019 D62E 2A 88 D0 LD HL,(INPOINT) ;get input pointer. +1020 D631 EB EX DE,HL +1021 D632 CD 4F D2 CALL NONBLANK ;get next non blank character. +1022 D635 FE 3D CP '=' ;only allow an '=' or '_' seperator. +1023 D637 CA 3F D6 JP Z,RENAME1 +1024 D63A FE 5F CP '_' +1025 D63C C2 73 D6 JP NZ,RENAME5 +1026 D63F EB RENAME1:EX DE,HL +1027 D640 23 INC HL ;ok, skip seperator. +1028 D641 22 88 D0 LD (INPOINT),HL ;save input line pointer. +1029 D644 CD 5E D2 CALL CONVFST ;convert this second file name now. +1030 D647 C2 73 D6 JP NZ,RENAME5 ;again, no wild cards. +1031 D64A F1 POP AF ;if a drive was specified, then it +1032 D64B 47 LD B,A ;must be the same as before. +1033 D64C 21 F0 D7 LD HL,CHGDRV +1034 D64F 7E LD A,(HL) +1035 D650 B7 OR A +1036 D651 CA 59 D6 JP Z,RENAME2 +1037 D654 B8 CP B +1038 D655 70 LD (HL),B +1039 D656 C2 73 D6 JP NZ,RENAME5 ;they were different, error. +1040 D659 70 RENAME2:LD (HL),B ; reset as per the first file specification. +1041 D65A AF XOR A +1042 D65B 32 CD D7 LD (FCB),A ;clear the drive byte of the fcb. +1043 D65E CD E9 D0 RENAME3:CALL SRCHFCB ;and go look for second file. +1044 D661 CA 6D D6 JP Z,RENAME4 ;doesn't exist? +1045 D664 11 CD D7 LD DE,FCB +1046 D667 CD 0E D1 CALL RENAM ;ok, rename the file. +1047 D66A C3 86 D7 JP GETBACK +1048 D66D ; +1049 D66D ; Process rename errors here. +1050 D66D ; +1051 D66D CD EA D3 RENAME4:CALL NONE ;file not there. +1052 D670 C3 86 D7 JP GETBACK +1053 D673 CD 66 D4 RENAME5:CALL RESETDR ;bad command format. +1054 D676 C3 09 D2 JP SYNERR +1055 D679 01 82 D6 RENAME6:LD BC,EXISTS ;destination file already exists. +1056 D67C CD A7 D0 CALL PLINE +1057 D67F C3 86 D7 JP GETBACK +1058 D682 46696C652065EXISTS: .TEXT "File exists" +1058 D688 7869737473 +1059 D68D 00 .DB 0 +1060 D68E ; +1061 D68E ;************************************************************** +1062 D68E ;* +1063 D68E ;* U S E R C O M M A N D +1064 D68E ;* +1065 D68E ;************************************************************** +1066 D68E ; +1067 D68E CD F8 D3 USER: CALL DECODE ;get numeric value following command. +1068 D691 FE 10 CP 16 ;legal user number? +1069 D693 D2 09 D2 JP NC,SYNERR +1070 D696 5F LD E,A ;yes but is there anything else? +1071 D697 3A CE D7 LD A,(FCB+1) +1072 D69A FE 20 CP ' ' +1073 D69C CA 09 D2 JP Z,SYNERR ;yes, that is not allowed. +1074 D69F CD 15 D1 CALL GETSETUC ;ok, set user code. +1075 D6A2 C3 89 D7 JP GETBACK1 +1076 D6A5 ; +1077 D6A5 ;************************************************************** +1078 D6A5 ;* +1079 D6A5 ;* T R A N S I A N T P R O G R A M C O M M A N D +1080 D6A5 ;* +1081 D6A5 ;************************************************************** +1082 D6A5 ; +1083 D6A5 CD F5 D1 UNKNOWN:CALL VERIFY ;check for valid system (why?). +1084 D6A8 3A CE D7 LD A,(FCB+1) ;anything to execute? +1085 D6AB FE 20 CP ' ' +1086 D6AD C2 C4 D6 JP NZ,UNKWN1 +1087 D6B0 3A F0 D7 LD A,(CHGDRV) ;nope, only a drive change? +1088 D6B3 B7 OR A +1089 D6B4 CA 89 D7 JP Z,GETBACK1 ;neither??? +1090 D6B7 3D DEC A +1091 D6B8 32 EF D7 LD (CDRIVE),A ;ok, store new drive. +1092 D6BB CD 29 D1 CALL MOVECD ;set (TDRIVE) also. +1093 D6BE CD BD D0 CALL DSKSEL ;and select this drive. +1094 D6C1 C3 89 D7 JP GETBACK1 ;then return. +1095 D6C4 ; +1096 D6C4 ; Here a file name was typed. Prepare to execute it. +1097 D6C4 ; +1098 D6C4 11 D6 D7 UNKWN1: LD DE,FCB+9 ;an extension specified? +1099 D6C7 1A LD A,(DE) +1100 D6C8 FE 20 CP ' ' +1101 D6CA C2 09 D2 JP NZ,SYNERR ;yes, not allowed. +1102 D6CD D5 UNKWN2: PUSH DE +1103 D6CE CD 54 D4 CALL DSELECT ;select specified drive. +1104 D6D1 D1 POP DE +1105 D6D2 21 83 D7 LD HL,COMFILE ;set the extension to 'COM'. +1106 D6D5 CD 40 D4 CALL MOVE3 +1107 D6D8 CD D0 D0 CALL OPENFCB ;and open this file. +1108 D6DB CA 6B D7 JP Z,UNKWN9 ;not present? +1109 D6DE ; +1110 D6DE ; Load in the program. +1111 D6DE ; +1112 D6DE 21 00 01 LD HL,TBASE ;store the program starting here. +1113 D6E1 E5 UNKWN3: PUSH HL +1114 D6E2 EB EX DE,HL +1115 D6E3 CD D8 D1 CALL DMASET ;set transfer address. +1116 D6E6 11 CD D7 LD DE,FCB ;and read the next record. +1117 D6E9 CD F9 D0 CALL RDREC +1118 D6EC C2 01 D7 JP NZ,UNKWN4 ;end of file or read error? +1119 D6EF E1 POP HL ;nope, bump pointer for next sector. +1120 D6F0 11 80 00 LD DE,128 +1121 D6F3 19 ADD HL,DE +1122 D6F4 11 00 D0 LD DE,CBASE ;enough room for the whole file? +1123 D6F7 7D LD A,L +1124 D6F8 93 SUB E +1125 D6F9 7C LD A,H +1126 D6FA 9A SBC A,D +1127 D6FB D2 71 D7 JP NC,UNKWN0 ;no, it can't fit. +1128 D6FE C3 E1 D6 JP UNKWN3 +1129 D701 ; +1130 D701 ; Get here after finished reading. +1131 D701 ; +1132 D701 E1 UNKWN4: POP HL +1133 D702 3D DEC A ;normal end of file? +1134 D703 C2 71 D7 JP NZ,UNKWN0 +1135 D706 CD 66 D4 CALL RESETDR ;yes, reset previous drive. +1136 D709 CD 5E D2 CALL CONVFST ;convert the first file name that follows +1137 D70C 21 F0 D7 LD HL,CHGDRV ;command name. +1138 D70F E5 PUSH HL +1139 D710 7E LD A,(HL) ;set drive code in default fcb. +1140 D711 32 CD D7 LD (FCB),A +1141 D714 3E 10 LD A,16 ;put second name 16 bytes later. +1142 D716 CD 60 D2 CALL CONVERT ;convert second file name. +1143 D719 E1 POP HL +1144 D71A 7E LD A,(HL) ;and set the drive for this second file. +1145 D71B 32 DD D7 LD (FCB+16),A +1146 D71E AF XOR A ;clear record byte in fcb. +1147 D71F 32 ED D7 LD (FCB+32),A +1148 D722 11 5C 00 LD DE,TFCB ;move it into place at(005Ch). +1149 D725 21 CD D7 LD HL,FCB +1150 D728 06 21 LD B,33 +1151 D72A CD 42 D4 CALL HL2DE +1152 D72D 21 08 D0 LD HL,INBUFF+2 ;now move the remainder of the input +1153 D730 7E UNKWN5: LD A,(HL) ;line down to (0080h). Look for a non blank. +1154 D731 B7 OR A ;or a null. +1155 D732 CA 3E D7 JP Z,UNKWN6 +1156 D735 FE 20 CP ' ' +1157 D737 CA 3E D7 JP Z,UNKWN6 +1158 D73A 23 INC HL +1159 D73B C3 30 D7 JP UNKWN5 +1160 D73E ; +1161 D73E ; Do the line move now. It ends in a null byte. +1162 D73E ; +1163 D73E 06 00 UNKWN6: LD B,0 ;keep a character count. +1164 D740 11 81 00 LD DE,TBUFF+1 ;data gets put here. +1165 D743 7E UNKWN7: LD A,(HL) ;move it now. +1166 D744 12 LD (DE),A +1167 D745 B7 OR A +1168 D746 CA 4F D7 JP Z,UNKWN8 +1169 D749 04 INC B +1170 D74A 23 INC HL +1171 D74B 13 INC DE +1172 D74C C3 43 D7 JP UNKWN7 +1173 D74F 78 UNKWN8: LD A,B ;now store the character count. +1174 D750 32 80 00 LD (TBUFF),A +1175 D753 CD 98 D0 CALL CRLF ;clean up the screen. +1176 D756 CD D5 D1 CALL STDDMA ;set standard transfer address. +1177 D759 CD 1A D1 CALL SETCDRV ;reset current drive. +1178 D75C CD 00 01 CALL TBASE ;and execute the program. +1179 D75F ; +1180 D75F ; Transiant programs return here (or reboot). +1181 D75F ; +1182 D75F 31 AB D7 LD SP,BATCH ;set stack first off. +1183 D762 CD 29 D1 CALL MOVECD ;move current drive into place (TDRIVE). +1184 D765 CD BD D0 CALL DSKSEL ;and reselect it. +1185 D768 C3 82 D3 JP CMMND1 ;back to comand mode. +1186 D76B ; +1187 D76B ; Get here if some error occured. +1188 D76B ; +1189 D76B CD 66 D4 UNKWN9: CALL RESETDR ;inproper format. +1190 D76E C3 09 D2 JP SYNERR +1191 D771 01 7A D7 UNKWN0: LD BC,BADLOAD ;read error or won't fit. +1192 D774 CD A7 D0 CALL PLINE +1193 D777 C3 86 D7 JP GETBACK +1194 D77A 426164206C6FBADLOAD:.TEXT "Bad load" +1194 D780 6164 +1195 D782 00 .DB 0 +1196 D783 43 4F 4D COMFILE:.TEXT "COM" ;command file extension. +1197 D786 ; +1198 D786 ; Get here to return to command level. We will reset the +1199 D786 ; previous active drive and then either return to command +1200 D786 ; level directly or print error message and then return. +1201 D786 ; +1202 D786 CD 66 D4 GETBACK:CALL RESETDR ;reset previous drive. +1203 D789 CD 5E D2 GETBACK1: CALL CONVFST ;convert first name in (FCB). +1204 D78C 3A CE D7 LD A,(FCB+1) ;if this was just a drive change request, +1205 D78F D6 20 SUB ' ' ;make sure it was valid. +1206 D791 21 F0 D7 LD HL,CHGDRV +1207 D794 B6 OR (HL) +1208 D795 C2 09 D2 JP NZ,SYNERR +1209 D798 C3 82 D3 JP CMMND1 ;ok, return to command level. +1210 D79B ; +1211 D79B ; ccp stack area. +1212 D79B ; +1213 D79B 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +1213 D7A1 00000000000000000000 +1214 D7AB CCPSTACK .EQU $ ;end of ccp stack area. +1215 D7AB ; +1216 D7AB ; Batch (or SUBMIT) processing information storage. +1217 D7AB ; +1218 D7AB 00 BATCH: .DB 0 ;batch mode flag (0=not active). +1219 D7AC 00 BATCHFCB: .DB 0, +1220 D7AD 242424202020 .TEXT "$$$ SUB" +1220 D7B3 2020535542 +1221 D7B8 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +1221 D7BE 000000000000000000000000000000 +1222 D7CD ; +1223 D7CD ; File control block setup by the CCP. +1224 D7CD ; +1225 D7CD 00 FCB: .DB 0 +1226 D7CE 202020202020 .TEXT " " +1226 D7D4 2020202020 +1227 D7D9 0000000000 .DB 0,0,0,0,0 +1228 D7DE 202020202020 .TEXT " " +1228 D7E4 2020202020 +1229 D7E9 0000000000 .DB 0,0,0,0,0 +1230 D7EE 00 RTNCODE:.DB 0 ;status returned from bdos call. +1231 D7EF 00 CDRIVE: .DB 0 ;currently active drive. +1232 D7F0 00 CHGDRV: .DB 0 ;change in drives flag (0=no change). +1233 D7F1 00 00 NBYTES: .DW 0 ;byte counter used by TYPE. +1234 D7F3 ; +1235 D7F3 ; Room for expansion? +1236 D7F3 ; +1237 D7F3 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0 +1237 D7F9 00000000000000 +1238 D800 ; +1239 D800 ; Note that the following six bytes must match those at +1240 D800 ; (PATTRN1) or cp/m will HALT. Why? +1241 D800 ; +1242 D800 001600000000PATTRN2:.DB 0,22,0,0,0,0 ;(* serial number bytes *). +1243 D806 ; +1244 D806 ;************************************************************** +1245 D806 ;* +1246 D806 ;* B D O S E N T R Y +1247 D806 ;* +1248 D806 ;************************************************************** +1249 D806 ; +1250 D806 C3 11 D8 FBASE: JP FBASE1 +1251 D809 ; +1252 D809 ; Bdos error table. +1253 D809 ; +1254 D809 99 D8 BADSCTR:.DW ERROR1 ;bad sector on read or write. +1255 D80B A5 D8 BADSLCT:.DW ERROR2 ;bad disk select. +1256 D80D AB D8 RODISK: .DW ERROR3 ;disk is read only. +1257 D80F B1 D8 ROFILE: .DW ERROR4 ;file is read only. +1258 D811 ; +1259 D811 ; Entry into bdos. (DE) or (E) are the parameters passed. The +1260 D811 ; function number desired is in register (C). +1261 D811 ; +1262 D811 EB FBASE1: EX DE,HL ;save the (DE) parameters. +1263 D812 22 43 DB LD (PARAMS),HL +1264 D815 EB EX DE,HL +1265 D816 7B LD A,E ;and save register (E) in particular. +1266 D817 32 D6 E5 LD (EPARAM),A +1267 D81A 21 00 00 LD HL,0 +1268 D81D 22 45 DB LD (STATUS),HL ;clear return status. +1269 D820 39 ADD HL,SP +1270 D821 22 0F DB LD (USRSTACK),HL ;save users stack pointer. +1271 D824 31 41 DB LD SP,STKAREA ;and set our own. +1272 D827 AF XOR A ;clear auto select storage space. +1273 D828 32 E0 E5 LD (AUTOFLAG),A +1274 D82B 32 DE E5 LD (AUTO),A +1275 D82E 21 74 E5 LD HL,GOBACK ;set return address. +1276 D831 E5 PUSH HL +1277 D832 79 LD A,C ;get function number. +1278 D833 FE 29 CP NFUNCTS ;valid function number? +1279 D835 D0 RET NC +1280 D836 4B LD C,E ;keep single register function here. +1281 D837 21 47 D8 LD HL,FUNCTNS ;now look thru the function table. +1282 D83A 5F LD E,A +1283 D83B 16 00 LD D,0 ;(DE)=function number. +1284 D83D 19 ADD HL,DE +1285 D83E 19 ADD HL,DE ;(HL)=(start of table)+2*(function number). +1286 D83F 5E LD E,(HL) +1287 D840 23 INC HL +1288 D841 56 LD D,(HL) ;now (DE)=address for this function. +1289 D842 2A 43 DB LD HL,(PARAMS) ;retrieve parameters. +1290 D845 EB EX DE,HL ;now (DE) has the original parameters. +1291 D846 E9 JP (HL) ;execute desired function. +1292 D847 ; +1293 D847 ; BDOS function jump table. +1294 D847 ; +1295 D847 NFUNCTS .EQU 41 ;number of functions in followin table. +1296 D847 ; +1297 D847 03E6C8DA90D9FUNCTNS:.DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB +1297 D84D CEDA12E60FE6D4DAEDDA +1298 D857 F3DAF8DAE1D9 .DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL +1298 D85D FEDA7EE483E445E49CE4 +1299 D867 A5E4ABE4C8E4 .DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE +1299 D86D D7E4E0E4E6E4ECE4 +1300 D875 F5E4FEE404E5 .DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR +1300 D87B 0AE511E52CDD17E51DE5 +1301 D885 26E52DE541E5 .DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN +1301 D88B 47E54DE50EE453E504DB +1302 D895 04 DB 9B E5 .DW RTN,WTSPECL +1303 D899 ; +1304 D899 ; Bdos error message section. +1305 D899 ; +1306 D899 21 CA D8 ERROR1: LD HL,BADSEC ;bad sector message. +1307 D89C CD E5 D8 CALL PRTERR ;print it and get a 1 char responce. +1308 D89F FE 03 CP CNTRLC ;re-boot request (control-c)? +1309 D8A1 CA 00 00 JP Z,0 ;yes. +1310 D8A4 C9 RET ;no, return to retry i/o function. +1311 D8A5 ; +1312 D8A5 21 D5 D8 ERROR2: LD HL,BADSEL ;bad drive selected. +1313 D8A8 C3 B4 D8 JP ERROR5 +1314 D8AB ; +1315 D8AB 21 E1 D8 ERROR3: LD HL,DISKRO ;disk is read only. +1316 D8AE C3 B4 D8 JP ERROR5 +1317 D8B1 ; +1318 D8B1 21 DC D8 ERROR4: LD HL,FILERO ;file is read only. +1319 D8B4 ; +1320 D8B4 CD E5 D8 ERROR5: CALL PRTERR +1321 D8B7 C3 00 00 JP 0 ;always reboot on these errors. +1322 D8BA ; +1323 D8BA 42646F732045BDOSERR:.TEXT "Bdos Err On " +1323 D8C0 7272204F6E20 +1324 D8C6 20 3A 20 24 BDOSDRV:.TEXT " : $" +1325 D8CA 426164205365BADSEC: .TEXT "Bad Sector$" +1325 D8D0 63746F7224 +1326 D8D5 53656C656374BADSEL: .TEXT "Select$" +1326 D8DB 24 +1327 D8DC 46696C6520 FILERO: .TEXT "File " +1328 D8E1 52 2F 4F 24 DISKRO: .TEXT "R/O$" +1329 D8E5 ; +1330 D8E5 ; Print bdos error message. +1331 D8E5 ; +1332 D8E5 E5 PRTERR: PUSH HL ;save second message pointer. +1333 D8E6 CD C9 D9 CALL OUTCRLF ;send (cr)(lf). +1334 D8E9 3A 42 DB LD A,(ACTIVE) ;get active drive. +1335 D8EC C6 41 ADD A,'A' ;make ascii. +1336 D8EE 32 C6 D8 LD (BDOSDRV),A ;and put in message. +1337 D8F1 01 BA D8 LD BC,BDOSERR ;and print it. +1338 D8F4 CD D3 D9 CALL PRTMESG +1339 D8F7 C1 POP BC ;print second message line now. +1340 D8F8 CD D3 D9 CALL PRTMESG +1341 D8FB ; +1342 D8FB ; Get an input character. We will check our 1 character +1343 D8FB ; buffer first. This may be set by the console status routine. +1344 D8FB ; +1345 D8FB 21 0E DB GETCHAR:LD HL,CHARBUF ;check character buffer. +1346 D8FE 7E LD A,(HL) ;anything present already? +1347 D8FF 36 00 LD (HL),0 ;...either case clear it. +1348 D901 B7 OR A +1349 D902 C0 RET NZ ;yes, use it. +1350 D903 C3 09 E6 JP CONIN ;nope, go get a character responce. +1351 D906 ; +1352 D906 ; Input and echo a character. +1353 D906 ; +1354 D906 CD FB D8 GETECHO:CALL GETCHAR ;input a character. +1355 D909 CD 14 D9 CALL CHKCHAR ;carriage control? +1356 D90C D8 RET C ;no, a regular control char so don't echo. +1357 D90D F5 PUSH AF ;ok, save character now. +1358 D90E 4F LD C,A +1359 D90F CD 90 D9 CALL OUTCON ;and echo it. +1360 D912 F1 POP AF ;get character and return. +1361 D913 C9 RET +1362 D914 ; +1363 D914 ; Check character in (A). Set the zero flag on a carriage +1364 D914 ; control character and the carry flag on any other control +1365 D914 ; character. +1366 D914 ; +1367 D914 FE 0D CHKCHAR:CP CR ;check for carriage return, line feed, backspace, +1368 D916 C8 RET Z ;or a tab. +1369 D917 FE 0A CP LF +1370 D919 C8 RET Z +1371 D91A FE 09 CP TAB +1372 D91C C8 RET Z +1373 D91D FE 08 CP BS +1374 D91F C8 RET Z +1375 D920 FE 20 CP ' ' ;other control char? Set carry flag. +1376 D922 C9 RET +1377 D923 ; +1378 D923 ; Check the console during output. Halt on a control-s, then +1379 D923 ; reboot on a control-c. If anything else is ready, clear the +1380 D923 ; zero flag and return (the calling routine may want to do +1381 D923 ; something). +1382 D923 ; +1383 D923 3A 0E DB CKCONSOL: LD A,(CHARBUF) ;check buffer. +1384 D926 B7 OR A ;if anything, just return without checking. +1385 D927 C2 45 D9 JP NZ,CKCON2 +1386 D92A CD 06 E6 CALL CONST ;nothing in buffer. Check console. +1387 D92D E6 01 AND 01H ;look at bit 0. +1388 D92F C8 RET Z ;return if nothing. +1389 D930 CD 09 E6 CALL CONIN ;ok, get it. +1390 D933 FE 13 CP CNTRLS ;if not control-s, return with zero cleared. +1391 D935 C2 42 D9 JP NZ,CKCON1 +1392 D938 CD 09 E6 CALL CONIN ;halt processing until another char +1393 D93B FE 03 CP CNTRLC ;is typed. Control-c? +1394 D93D CA 00 00 JP Z,0 ;yes, reboot now. +1395 D940 AF XOR A ;no, just pretend nothing was ever ready. +1396 D941 C9 RET +1397 D942 32 0E DB CKCON1: LD (CHARBUF),A ;save character in buffer for later processing. +1398 D945 3E 01 CKCON2: LD A,1 ;set (A) to non zero to mean something is ready. +1399 D947 C9 RET +1400 D948 ; +1401 D948 ; Output (C) to the screen. If the printer flip-flop flag +1402 D948 ; is set, we will send character to printer also. The console +1403 D948 ; will be checked in the process. +1404 D948 ; +1405 D948 3A 0A DB OUTCHAR:LD A,(OUTFLAG) ;check output flag. +1406 D94B B7 OR A ;anything and we won't generate output. +1407 D94C C2 62 D9 JP NZ,OUTCHR1 +1408 D94F C5 PUSH BC +1409 D950 CD 23 D9 CALL CKCONSOL ;check console (we don't care whats there). +1410 D953 C1 POP BC +1411 D954 C5 PUSH BC +1412 D955 CD 0C E6 CALL CONOUT ;output (C) to the screen. +1413 D958 C1 POP BC +1414 D959 C5 PUSH BC +1415 D95A 3A 0D DB LD A,(PRTFLAG) ;check printer flip-flop flag. +1416 D95D B7 OR A +1417 D95E C4 0F E6 CALL NZ,LIST ;print it also if non-zero. +1418 D961 C1 POP BC +1419 D962 79 OUTCHR1:LD A,C ;update cursors position. +1420 D963 21 0C DB LD HL,CURPOS +1421 D966 FE 7F CP DEL ;rubouts don't do anything here. +1422 D968 C8 RET Z +1423 D969 34 INC (HL) ;bump line pointer. +1424 D96A FE 20 CP ' ' ;and return if a normal character. +1425 D96C D0 RET NC +1426 D96D 35 DEC (HL) ;restore and check for the start of the line. +1427 D96E 7E LD A,(HL) +1428 D96F B7 OR A +1429 D970 C8 RET Z ;ingnore control characters at the start of the line. +1430 D971 79 LD A,C +1431 D972 FE 08 CP BS ;is it a backspace? +1432 D974 C2 79 D9 JP NZ,OUTCHR2 +1433 D977 35 DEC (HL) ;yes, backup pointer. +1434 D978 C9 RET +1435 D979 FE 0A OUTCHR2:CP LF ;is it a line feed? +1436 D97B C0 RET NZ ;ignore anything else. +1437 D97C 36 00 LD (HL),0 ;reset pointer to start of line. +1438 D97E C9 RET +1439 D97F ; +1440 D97F ; Output (A) to the screen. If it is a control character +1441 D97F ; (other than carriage control), use ^x format. +1442 D97F ; +1443 D97F 79 SHOWIT: LD A,C +1444 D980 CD 14 D9 CALL CHKCHAR ;check character. +1445 D983 D2 90 D9 JP NC,OUTCON ;not a control, use normal output. +1446 D986 F5 PUSH AF +1447 D987 0E 5E LD C,'^' ;for a control character, preceed it with '^'. +1448 D989 CD 48 D9 CALL OUTCHAR +1449 D98C F1 POP AF +1450 D98D F6 40 OR '@' ;and then use the letter equivelant. +1451 D98F 4F LD C,A +1452 D990 ; +1453 D990 ; Function to output (C) to the console device and expand tabs +1454 D990 ; if necessary. +1455 D990 ; +1456 D990 79 OUTCON: LD A,C +1457 D991 FE 09 CP TAB ;is it a tab? +1458 D993 C2 48 D9 JP NZ,OUTCHAR ;use regular output. +1459 D996 0E 20 OUTCON1:LD C,' ' ;yes it is, use spaces instead. +1460 D998 CD 48 D9 CALL OUTCHAR +1461 D99B 3A 0C DB LD A,(CURPOS) ;go until the cursor is at a multiple of 8 +1462 D99E +1463 D99E E6 07 AND 07H ;position. +1464 D9A0 C2 96 D9 JP NZ,OUTCON1 +1465 D9A3 C9 RET +1466 D9A4 ; +1467 D9A4 ; Echo a backspace character. Erase the prevoius character +1468 D9A4 ; on the screen. +1469 D9A4 ; +1470 D9A4 CD AC D9 BACKUP: CALL BACKUP1 ;backup the screen 1 place. +1471 D9A7 0E 20 LD C,' ' ;then blank that character. +1472 D9A9 CD 0C E6 CALL CONOUT +1473 D9AC 0E 08 BACKUP1:LD C,BS ;then back space once more. +1474 D9AE C3 0C E6 JP CONOUT +1475 D9B1 ; +1476 D9B1 ; Signal a deleted line. Print a '#' at the end and start +1477 D9B1 ; over. +1478 D9B1 ; +1479 D9B1 0E 23 NEWLINE:LD C,'#' +1480 D9B3 CD 48 D9 CALL OUTCHAR ;print this. +1481 D9B6 CD C9 D9 CALL OUTCRLF ;start new line. +1482 D9B9 3A 0C DB NEWLN1: LD A,(CURPOS) ;move the cursor to the starting position. +1483 D9BC 21 0B DB LD HL,STARTING +1484 D9BF BE CP (HL) +1485 D9C0 D0 RET NC ;there yet? +1486 D9C1 0E 20 LD C,' ' +1487 D9C3 CD 48 D9 CALL OUTCHAR ;nope, keep going. +1488 D9C6 C3 B9 D9 JP NEWLN1 +1489 D9C9 ; +1490 D9C9 ; Output a (cr) (lf) to the console device (screen). +1491 D9C9 ; +1492 D9C9 0E 0D OUTCRLF:LD C,CR +1493 D9CB CD 48 D9 CALL OUTCHAR +1494 D9CE 0E 0A LD C,LF +1495 D9D0 C3 48 D9 JP OUTCHAR +1496 D9D3 ; +1497 D9D3 ; Print message pointed to by (BC). It will end with a '$'. +1498 D9D3 ; +1499 D9D3 0A PRTMESG:LD A,(BC) ;check for terminating character. +1500 D9D4 FE 24 CP '$' +1501 D9D6 C8 RET Z +1502 D9D7 03 INC BC +1503 D9D8 C5 PUSH BC ;otherwise, bump pointer and print it. +1504 D9D9 4F LD C,A +1505 D9DA CD 90 D9 CALL OUTCON +1506 D9DD C1 POP BC +1507 D9DE C3 D3 D9 JP PRTMESG +1508 D9E1 ; +1509 D9E1 ; Function to execute a buffered read. +1510 D9E1 ; +1511 D9E1 3A 0C DB RDBUFF: LD A,(CURPOS) ;use present location as starting one. +1512 D9E4 32 0B DB LD (STARTING),A +1513 D9E7 2A 43 DB LD HL,(PARAMS) ;get the maximum buffer space. +1514 D9EA 4E LD C,(HL) +1515 D9EB 23 INC HL ;point to first available space. +1516 D9EC E5 PUSH HL ;and save. +1517 D9ED 06 00 LD B,0 ;keep a character count. +1518 D9EF C5 RDBUF1: PUSH BC +1519 D9F0 E5 PUSH HL +1520 D9F1 CD FB D8 RDBUF2: CALL GETCHAR ;get the next input character. +1521 D9F4 E6 7F AND 7FH ;strip bit 7. +1522 D9F6 E1 POP HL ;reset registers. +1523 D9F7 C1 POP BC +1524 D9F8 FE 0D CP CR ;en of the line? +1525 D9FA CA C1 DA JP Z,RDBUF17 +1526 D9FD FE 0A CP LF +1527 D9FF CA C1 DA JP Z,RDBUF17 +1528 DA02 FE 08 CP BS ;how about a backspace? +1529 DA04 C2 16 DA JP NZ,RDBUF3 +1530 DA07 78 LD A,B ;yes, but ignore at the beginning of the line. +1531 DA08 B7 OR A +1532 DA09 CA EF D9 JP Z,RDBUF1 +1533 DA0C 05 DEC B ;ok, update counter. +1534 DA0D 3A 0C DB LD A,(CURPOS) ;if we backspace to the start of the line, +1535 DA10 32 0A DB LD (OUTFLAG),A ;treat as a cancel (control-x). +1536 DA13 C3 70 DA JP RDBUF10 +1537 DA16 FE 7F RDBUF3: CP DEL ;user typed a rubout? +1538 DA18 C2 26 DA JP NZ,RDBUF4 +1539 DA1B 78 LD A,B ;ignore at the start of the line. +1540 DA1C B7 OR A +1541 DA1D CA EF D9 JP Z,RDBUF1 +1542 DA20 7E LD A,(HL) ;ok, echo the prevoius character. +1543 DA21 05 DEC B ;and reset pointers (counters). +1544 DA22 2B DEC HL +1545 DA23 C3 A9 DA JP RDBUF15 +1546 DA26 FE 05 RDBUF4: CP CNTRLE ;physical end of line? +1547 DA28 C2 37 DA JP NZ,RDBUF5 +1548 DA2B C5 PUSH BC ;yes, do it. +1549 DA2C E5 PUSH HL +1550 DA2D CD C9 D9 CALL OUTCRLF +1551 DA30 AF XOR A ;and update starting position. +1552 DA31 32 0B DB LD (STARTING),A +1553 DA34 C3 F1 D9 JP RDBUF2 +1554 DA37 FE 10 RDBUF5: CP CNTRLP ;control-p? +1555 DA39 C2 48 DA JP NZ,RDBUF6 +1556 DA3C E5 PUSH HL ;yes, flip the print flag filp-flop byte. +1557 DA3D 21 0D DB LD HL,PRTFLAG +1558 DA40 3E 01 LD A,1 ;PRTFLAG=1-PRTFLAG +1559 DA42 96 SUB (HL) +1560 DA43 77 LD (HL),A +1561 DA44 E1 POP HL +1562 DA45 C3 EF D9 JP RDBUF1 +1563 DA48 FE 18 RDBUF6: CP CNTRLX ;control-x (cancel)? +1564 DA4A C2 5F DA JP NZ,RDBUF8 +1565 DA4D E1 POP HL +1566 DA4E 3A 0B DB RDBUF7: LD A,(STARTING) ;yes, backup the cursor to here. +1567 DA51 21 0C DB LD HL,CURPOS +1568 DA54 BE CP (HL) +1569 DA55 D2 E1 D9 JP NC,RDBUFF ;done yet? +1570 DA58 35 DEC (HL) ;no, decrement pointer and output back up one space. +1571 DA59 CD A4 D9 CALL BACKUP +1572 DA5C C3 4E DA JP RDBUF7 +1573 DA5F FE 15 RDBUF8: CP CNTRLU ;cntrol-u (cancel line)? +1574 DA61 C2 6B DA JP NZ,RDBUF9 +1575 DA64 CD B1 D9 CALL NEWLINE ;start a new line. +1576 DA67 E1 POP HL +1577 DA68 C3 E1 D9 JP RDBUFF +1578 DA6B FE 12 RDBUF9: CP CNTRLR ;control-r? +1579 DA6D C2 A6 DA JP NZ,RDBUF14 +1580 DA70 C5 RDBUF10:PUSH BC ;yes, start a new line and retype the old one. +1581 DA71 CD B1 D9 CALL NEWLINE +1582 DA74 C1 POP BC +1583 DA75 E1 POP HL +1584 DA76 E5 PUSH HL +1585 DA77 C5 PUSH BC +1586 DA78 78 RDBUF11:LD A,B ;done whole line yet? +1587 DA79 B7 OR A +1588 DA7A CA 8A DA JP Z,RDBUF12 +1589 DA7D 23 INC HL ;nope, get next character. +1590 DA7E 4E LD C,(HL) +1591 DA7F 05 DEC B ;count it. +1592 DA80 C5 PUSH BC +1593 DA81 E5 PUSH HL +1594 DA82 CD 7F D9 CALL SHOWIT ;and display it. +1595 DA85 E1 POP HL +1596 DA86 C1 POP BC +1597 DA87 C3 78 DA JP RDBUF11 +1598 DA8A E5 RDBUF12:PUSH HL ;done with line. If we were displaying +1599 DA8B 3A 0A DB LD A,(OUTFLAG) ;then update cursor position. +1600 DA8E B7 OR A +1601 DA8F CA F1 D9 JP Z,RDBUF2 +1602 DA92 21 0C DB LD HL,CURPOS ;because this line is shorter, we must +1603 DA95 96 SUB (HL) ;back up the cursor (not the screen however) +1604 DA96 32 0A DB LD (OUTFLAG),A ;some number of positions. +1605 DA99 CD A4 D9 RDBUF13:CALL BACKUP ;note that as long as (OUTFLAG) is non +1606 DA9C 21 0A DB LD HL,OUTFLAG ;zero, the screen will not be changed. +1607 DA9F 35 DEC (HL) +1608 DAA0 C2 99 DA JP NZ,RDBUF13 +1609 DAA3 C3 F1 D9 JP RDBUF2 ;now just get the next character. +1610 DAA6 ; +1611 DAA6 ; Just a normal character, put this in our buffer and echo. +1612 DAA6 ; +1613 DAA6 23 RDBUF14:INC HL +1614 DAA7 77 LD (HL),A ;store character. +1615 DAA8 04 INC B ;and count it. +1616 DAA9 C5 RDBUF15:PUSH BC +1617 DAAA E5 PUSH HL +1618 DAAB 4F LD C,A ;echo it now. +1619 DAAC CD 7F D9 CALL SHOWIT +1620 DAAF E1 POP HL +1621 DAB0 C1 POP BC +1622 DAB1 7E LD A,(HL) ;was it an abort request? +1623 DAB2 FE 03 CP CNTRLC ;control-c abort? +1624 DAB4 78 LD A,B +1625 DAB5 C2 BD DA JP NZ,RDBUF16 +1626 DAB8 FE 01 CP 1 ;only if at start of line. +1627 DABA CA 00 00 JP Z,0 +1628 DABD B9 RDBUF16:CP C ;nope, have we filled the buffer? +1629 DABE DA EF D9 JP C,RDBUF1 +1630 DAC1 E1 RDBUF17:POP HL ;yes end the line and return. +1631 DAC2 70 LD (HL),B +1632 DAC3 0E 0D LD C,CR +1633 DAC5 C3 48 D9 JP OUTCHAR ;output (cr) and return. +1634 DAC8 ; +1635 DAC8 ; Function to get a character from the console device. +1636 DAC8 ; +1637 DAC8 CD 06 D9 GETCON: CALL GETECHO ;get and echo. +1638 DACB C3 01 DB JP SETSTAT ;save status and return. +1639 DACE ; +1640 DACE ; Function to get a character from the tape reader device. +1641 DACE ; +1642 DACE CD 15 E6 GETRDR: CALL READER ;get a character from reader, set status and return. +1643 DAD1 C3 01 DB JP SETSTAT +1644 DAD4 ; +1645 DAD4 ; Function to perform direct console i/o. If (C) contains (FF) +1646 DAD4 ; then this is an input request. If (C) contains (FE) then +1647 DAD4 ; this is a status request. Otherwise we are to output (C). +1648 DAD4 ; +1649 DAD4 79 DIRCIO: LD A,C ;test for (FF). +1650 DAD5 3C INC A +1651 DAD6 CA E0 DA JP Z,DIRC1 +1652 DAD9 3C INC A ;test for (FE). +1653 DADA CA 06 E6 JP Z,CONST +1654 DADD C3 0C E6 JP CONOUT ;just output (C). +1655 DAE0 CD 06 E6 DIRC1: CALL CONST ;this is an input request. +1656 DAE3 B7 OR A +1657 DAE4 CA 91 E5 JP Z,GOBACK1 ;not ready? Just return (directly). +1658 DAE7 CD 09 E6 CALL CONIN ;yes, get character. +1659 DAEA C3 01 DB JP SETSTAT ;set status and return. +1660 DAED ; +1661 DAED ; Function to return the i/o byte. +1662 DAED ; +1663 DAED 3A 03 00 GETIOB: LD A,(IOBYTE) +1664 DAF0 C3 01 DB JP SETSTAT +1665 DAF3 ; +1666 DAF3 ; Function to set the i/o byte. +1667 DAF3 ; +1668 DAF3 21 03 00 SETIOB: LD HL,IOBYTE +1669 DAF6 71 LD (HL),C +1670 DAF7 C9 RET +1671 DAF8 ; +1672 DAF8 ; Function to print the character string pointed to by (DE) +1673 DAF8 ; on the console device. The string ends with a '$'. +1674 DAF8 ; +1675 DAF8 EB PRTSTR: EX DE,HL +1676 DAF9 4D LD C,L +1677 DAFA 44 LD B,H ;now (BC) points to it. +1678 DAFB C3 D3 D9 JP PRTMESG +1679 DAFE ; +1680 DAFE ; Function to interigate the console device. +1681 DAFE ; +1682 DAFE CD 23 D9 GETCSTS:CALL CKCONSOL +1683 DB01 ; +1684 DB01 ; Get here to set the status and return to the cleanup +1685 DB01 ; section. Then back to the user. +1686 DB01 ; +1687 DB01 32 45 DB SETSTAT:LD (STATUS),A +1688 DB04 C9 RTN: RET +1689 DB05 ; +1690 DB05 ; Set the status to 1 (read or write error code). +1691 DB05 ; +1692 DB05 3E 01 IOERR1: LD A,1 +1693 DB07 C3 01 DB JP SETSTAT +1694 DB0A ; +1695 DB0A 00 OUTFLAG:.DB 0 ;output flag (non zero means no output). +1696 DB0B 02 STARTING: .DB 2 ;starting position for cursor. +1697 DB0C 00 CURPOS: .DB 0 ;cursor position (0=start of line). +1698 DB0D 00 PRTFLAG:.DB 0 ;printer flag (control-p toggle). List if non zero. +1699 DB0E 00 CHARBUF:.DB 0 ;single input character buffer. +1700 DB0F ; +1701 DB0F ; Stack area for BDOS calls. +1702 DB0F ; +1703 DB0F 00 00 USRSTACK: .DW 0 ;save users stack pointer here. +1704 DB11 ; +1705 DB11 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +1705 DB17 000000000000000000000000000000000000 +1706 DB29 000000000000 .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +1706 DB2F 000000000000000000000000000000000000 +1707 DB41 STKAREA .EQU $ ;end of stack area. +1708 DB41 ; +1709 DB41 00 USERNO: .DB 0 ;current user number. +1710 DB42 00 ACTIVE: .DB 0 ;currently active drive. +1711 DB43 00 00 PARAMS: .DW 0 ;save (DE) parameters here on entry. +1712 DB45 00 00 STATUS: .DW 0 ;status returned from bdos function. +1713 DB47 ; +1714 DB47 ; Select error occured, jump to error routine. +1715 DB47 ; +1716 DB47 21 0B D8 SLCTERR:LD HL,BADSLCT +1717 DB4A ; +1718 DB4A ; Jump to (HL) indirectly. +1719 DB4A ; +1720 DB4A 5E JUMPHL: LD E,(HL) +1721 DB4B 23 INC HL +1722 DB4C 56 LD D,(HL) ;now (DE) contain the desired address. +1723 DB4D EB EX DE,HL +1724 DB4E E9 JP (HL) +1725 DB4F ; +1726 DB4F ; Block move. (DE) to (HL), (C) bytes total. +1727 DB4F ; +1728 DB4F 0C DE2HL: INC C ;is count down to zero? +1729 DB50 0D DE2HL1: DEC C +1730 DB51 C8 RET Z ;yes, we are done. +1731 DB52 1A LD A,(DE) ;no, move one more byte. +1732 DB53 77 LD (HL),A +1733 DB54 13 INC DE +1734 DB55 23 INC HL +1735 DB56 C3 50 DB JP DE2HL1 ;and repeat. +1736 DB59 ; +1737 DB59 ; Select the desired drive. +1738 DB59 ; +1739 DB59 3A 42 DB SELECT: LD A,(ACTIVE) ;get active disk. +1740 DB5C 4F LD C,A +1741 DB5D CD 1B E6 CALL SELDSK ;select it. +1742 DB60 7C LD A,H ;valid drive? +1743 DB61 B5 OR L ;valid drive? +1744 DB62 C8 RET Z ;return if not. +1745 DB63 ; +1746 DB63 ; Here, the BIOS returned the address of the parameter block +1747 DB63 ; in (HL). We will extract the necessary pointers and save them. +1748 DB63 ; +1749 DB63 5E LD E,(HL) ;yes, get address of translation table into (DE). +1750 DB64 23 INC HL +1751 DB65 56 LD D,(HL) +1752 DB66 23 INC HL +1753 DB67 22 B3 E5 LD (SCRATCH1),HL ;save pointers to scratch areas. +1754 DB6A 23 INC HL +1755 DB6B 23 INC HL +1756 DB6C 22 B5 E5 LD (SCRATCH2),HL ;ditto. +1757 DB6F 23 INC HL +1758 DB70 23 INC HL +1759 DB71 22 B7 E5 LD (SCRATCH3),HL ;ditto. +1760 DB74 23 INC HL +1761 DB75 23 INC HL +1762 DB76 EB EX DE,HL ;now save the translation table address. +1763 DB77 22 D0 E5 LD (XLATE),HL +1764 DB7A 21 B9 E5 LD HL,DIRBUF ;put the next 8 bytes here. +1765 DB7D 0E 08 LD C,8 ;they consist of the directory buffer +1766 DB7F CD 4F DB CALL DE2HL ;pointer, parameter block pointer, +1767 DB82 2A BB E5 LD HL,(DISKPB) ;check and allocation vectors. +1768 DB85 EB EX DE,HL +1769 DB86 21 C1 E5 LD HL,SECTORS ;move parameter block into our ram. +1770 DB89 0E 0F LD C,15 ;it is 15 bytes long. +1771 DB8B CD 4F DB CALL DE2HL +1772 DB8E 2A C6 E5 LD HL,(DSKSIZE) ;check disk size. +1773 DB91 7C LD A,H ;more than 256 blocks on this? +1774 DB92 21 DD E5 LD HL,BIGDISK +1775 DB95 36 FF LD (HL),0FFH ;set to samll. +1776 DB97 B7 OR A +1777 DB98 CA 9D DB JP Z,SELECT1 +1778 DB9B 36 00 LD (HL),0 ;wrong, set to large. +1779 DB9D 3E FF SELECT1:LD A,0FFH ;clear the zero flag. +1780 DB9F B7 OR A +1781 DBA0 C9 RET +1782 DBA1 ; +1783 DBA1 ; Routine to home the disk track head and clear pointers. +1784 DBA1 ; +1785 DBA1 CD 18 E6 HOMEDRV:CALL HOME ;home the head. +1786 DBA4 AF XOR A +1787 DBA5 2A B5 E5 LD HL,(SCRATCH2) ;set our track pointer also. +1788 DBA8 77 LD (HL),A +1789 DBA9 23 INC HL +1790 DBAA 77 LD (HL),A +1791 DBAB 2A B7 E5 LD HL,(SCRATCH3) ;and our sector pointer. +1792 DBAE 77 LD (HL),A +1793 DBAF 23 INC HL +1794 DBB0 77 LD (HL),A +1795 DBB1 C9 RET +1796 DBB2 ; +1797 DBB2 ; Do the actual disk read and check the error return status. +1798 DBB2 ; +1799 DBB2 CD 27 E6 DOREAD: CALL READ +1800 DBB5 C3 BB DB JP IORET +1801 DBB8 ; +1802 DBB8 ; Do the actual disk write and handle any bios error. +1803 DBB8 ; +1804 DBB8 CD 2A E6 DOWRITE:CALL WRITE +1805 DBBB B7 IORET: OR A +1806 DBBC C8 RET Z ;return unless an error occured. +1807 DBBD 21 09 D8 LD HL,BADSCTR ;bad read/write on this sector. +1808 DBC0 C3 4A DB JP JUMPHL +1809 DBC3 ; +1810 DBC3 ; Routine to select the track and sector that the desired +1811 DBC3 ; block number falls in. +1812 DBC3 ; +1813 DBC3 2A EA E5 TRKSEC: LD HL,(FILEPOS) ;get position of last accessed file +1814 DBC6 0E 02 LD C,2 ;in directory and compute sector #. +1815 DBC8 CD EA DC CALL SHIFTR ;sector #=file-position/4. +1816 DBCB 22 E5 E5 LD (BLKNMBR),HL ;save this as the block number of interest. +1817 DBCE 22 EC E5 LD (CKSUMTBL),HL ;what's it doing here too? +1818 DBD1 ; +1819 DBD1 ; if the sector number has already been set (BLKNMBR), enter +1820 DBD1 ; at this point. +1821 DBD1 ; +1822 DBD1 21 E5 E5 TRKSEC1:LD HL,BLKNMBR +1823 DBD4 4E LD C,(HL) ;move sector number into (BC). +1824 DBD5 23 INC HL +1825 DBD6 46 LD B,(HL) +1826 DBD7 2A B7 E5 LD HL,(SCRATCH3) ;get current sector number and +1827 DBDA 5E LD E,(HL) ;move this into (DE). +1828 DBDB 23 INC HL +1829 DBDC 56 LD D,(HL) +1830 DBDD 2A B5 E5 LD HL,(SCRATCH2) ;get current track number. +1831 DBE0 7E LD A,(HL) ;and this into (HL). +1832 DBE1 23 INC HL +1833 DBE2 66 LD H,(HL) +1834 DBE3 6F LD L,A +1835 DBE4 79 TRKSEC2:LD A,C ;is desired sector before current one? +1836 DBE5 93 SUB E +1837 DBE6 78 LD A,B +1838 DBE7 9A SBC A,D +1839 DBE8 D2 FA DB JP NC,TRKSEC3 +1840 DBEB E5 PUSH HL ;yes, decrement sectors by one track. +1841 DBEC 2A C1 E5 LD HL,(SECTORS) ;get sectors per track. +1842 DBEF 7B LD A,E +1843 DBF0 95 SUB L +1844 DBF1 5F LD E,A +1845 DBF2 7A LD A,D +1846 DBF3 9C SBC A,H +1847 DBF4 57 LD D,A ;now we have backed up one full track. +1848 DBF5 E1 POP HL +1849 DBF6 2B DEC HL ;adjust track counter. +1850 DBF7 C3 E4 DB JP TRKSEC2 +1851 DBFA E5 TRKSEC3:PUSH HL ;desired sector is after current one. +1852 DBFB 2A C1 E5 LD HL,(SECTORS) ;get sectors per track. +1853 DBFE 19 ADD HL,DE ;bump sector pointer to next track. +1854 DBFF DA 0F DC JP C,TRKSEC4 +1855 DC02 79 LD A,C ;is desired sector now before current one? +1856 DC03 95 SUB L +1857 DC04 78 LD A,B +1858 DC05 9C SBC A,H +1859 DC06 DA 0F DC JP C,TRKSEC4 +1860 DC09 EB EX DE,HL ;not yes, increment track counter +1861 DC0A E1 POP HL ;and continue until it is. +1862 DC0B 23 INC HL +1863 DC0C C3 FA DB JP TRKSEC3 +1864 DC0F ; +1865 DC0F ; here we have determined the track number that contains the +1866 DC0F ; desired sector. +1867 DC0F ; +1868 DC0F E1 TRKSEC4:POP HL ;get track number (HL). +1869 DC10 C5 PUSH BC +1870 DC11 D5 PUSH DE +1871 DC12 E5 PUSH HL +1872 DC13 EB EX DE,HL +1873 DC14 2A CE E5 LD HL,(OFFSET) ;adjust for first track offset. +1874 DC17 19 ADD HL,DE +1875 DC18 44 LD B,H +1876 DC19 4D LD C,L +1877 DC1A CD 1E E6 CALL SETTRK ;select this track. +1878 DC1D D1 POP DE ;reset current track pointer. +1879 DC1E 2A B5 E5 LD HL,(SCRATCH2) +1880 DC21 73 LD (HL),E +1881 DC22 23 INC HL +1882 DC23 72 LD (HL),D +1883 DC24 D1 POP DE +1884 DC25 2A B7 E5 LD HL,(SCRATCH3) ;reset the first sector on this track. +1885 DC28 73 LD (HL),E +1886 DC29 23 INC HL +1887 DC2A 72 LD (HL),D +1888 DC2B C1 POP BC +1889 DC2C 79 LD A,C ;now subtract the desired one. +1890 DC2D 93 SUB E ;to make it relative (1-# sectors/track). +1891 DC2E 4F LD C,A +1892 DC2F 78 LD A,B +1893 DC30 9A SBC A,D +1894 DC31 47 LD B,A +1895 DC32 2A D0 E5 LD HL,(XLATE) ;translate this sector according to this table. +1896 DC35 EB EX DE,HL +1897 DC36 CD 30 E6 CALL SECTRN ;let the bios translate it. +1898 DC39 4D LD C,L +1899 DC3A 44 LD B,H +1900 DC3B C3 21 E6 JP SETSEC ;and select it. +1901 DC3E ; +1902 DC3E ; Compute block number from record number (SAVNREC) and +1903 DC3E ; extent number (SAVEXT). +1904 DC3E ; +1905 DC3E 21 C3 E5 GETBLOCK: LD HL,BLKSHFT ;get logical to physical conversion. +1906 DC41 4E LD C,(HL) ;note that this is base 2 log of ratio. +1907 DC42 3A E3 E5 LD A,(SAVNREC) ;get record number. +1908 DC45 B7 GETBLK1:OR A ;compute (A)=(A)/2^BLKSHFT. +1909 DC46 1F RRA +1910 DC47 0D DEC C +1911 DC48 C2 45 DC JP NZ,GETBLK1 +1912 DC4B 47 LD B,A ;save result in (B). +1913 DC4C 3E 08 LD A,8 +1914 DC4E 96 SUB (HL) +1915 DC4F 4F LD C,A ;compute (C)=8-BLKSHFT. +1916 DC50 3A E2 E5 LD A,(SAVEXT) +1917 DC53 0D GETBLK2:DEC C ;compute (A)=SAVEXT*2^(8-BLKSHFT). +1918 DC54 CA 5C DC JP Z,GETBLK3 +1919 DC57 B7 OR A +1920 DC58 17 RLA +1921 DC59 C3 53 DC JP GETBLK2 +1922 DC5C 80 GETBLK3:ADD A,B +1923 DC5D C9 RET +1924 DC5E ; +1925 DC5E ; Routine to extract the (BC) block byte from the fcb pointed +1926 DC5E ; to by (PARAMS). If this is a big-disk, then these are 16 bit +1927 DC5E ; block numbers, else they are 8 bit numbers. +1928 DC5E ; Number is returned in (HL). +1929 DC5E ; +1930 DC5E 2A 43 DB EXTBLK: LD HL,(PARAMS) ;get fcb address. +1931 DC61 11 10 00 LD DE,16 ;block numbers start 16 bytes into fcb. +1932 DC64 19 ADD HL,DE +1933 DC65 09 ADD HL,BC +1934 DC66 3A DD E5 LD A,(BIGDISK) ;are we using a big-disk? +1935 DC69 B7 OR A +1936 DC6A CA 71 DC JP Z,EXTBLK1 +1937 DC6D 6E LD L,(HL) ;no, extract an 8 bit number from the fcb. +1938 DC6E 26 00 LD H,0 +1939 DC70 C9 RET +1940 DC71 09 EXTBLK1:ADD HL,BC ;yes, extract a 16 bit number. +1941 DC72 5E LD E,(HL) +1942 DC73 23 INC HL +1943 DC74 56 LD D,(HL) +1944 DC75 EB EX DE,HL ;return in (HL). +1945 DC76 C9 RET +1946 DC77 ; +1947 DC77 ; Compute block number. +1948 DC77 ; +1949 DC77 CD 3E DC COMBLK: CALL GETBLOCK +1950 DC7A 4F LD C,A +1951 DC7B 06 00 LD B,0 +1952 DC7D CD 5E DC CALL EXTBLK +1953 DC80 22 E5 E5 LD (BLKNMBR),HL +1954 DC83 C9 RET +1955 DC84 ; +1956 DC84 ; Check for a zero block number (unused). +1957 DC84 ; +1958 DC84 2A E5 E5 CHKBLK: LD HL,(BLKNMBR) +1959 DC87 7D LD A,L ;is it zero? +1960 DC88 B4 OR H +1961 DC89 C9 RET +1962 DC8A ; +1963 DC8A ; Adjust physical block (BLKNMBR) and convert to logical +1964 DC8A ; sector (LOGSECT). This is the starting sector of this block. +1965 DC8A ; The actual sector of interest is then added to this and the +1966 DC8A ; resulting sector number is stored back in (BLKNMBR). This +1967 DC8A ; will still have to be adjusted for the track number. +1968 DC8A ; +1969 DC8A 3A C3 E5 LOGICAL:LD A,(BLKSHFT) ;get log2(physical/logical sectors). +1970 DC8D 2A E5 E5 LD HL,(BLKNMBR) ;get physical sector desired. +1971 DC90 29 LOGICL1:ADD HL,HL ;compute logical sector number. +1972 DC91 3D DEC A ;note logical sectors are 128 bytes long. +1973 DC92 C2 90 DC JP NZ,LOGICL1 +1974 DC95 22 E7 E5 LD (LOGSECT),HL ;save logical sector. +1975 DC98 3A C4 E5 LD A,(BLKMASK) ;get block mask. +1976 DC9B 4F LD C,A +1977 DC9C 3A E3 E5 LD A,(SAVNREC) ;get next sector to access. +1978 DC9F A1 AND C ;extract the relative position within physical block. +1979 DCA0 B5 OR L ;and add it too logical sector. +1980 DCA1 6F LD L,A +1981 DCA2 22 E5 E5 LD (BLKNMBR),HL ;and store. +1982 DCA5 C9 RET +1983 DCA6 ; +1984 DCA6 ; Set (HL) to point to extent byte in fcb. +1985 DCA6 ; +1986 DCA6 2A 43 DB SETEXT: LD HL,(PARAMS) +1987 DCA9 11 0C 00 LD DE,12 ;it is the twelth byte. +1988 DCAC 19 ADD HL,DE +1989 DCAD C9 RET +1990 DCAE ; +1991 DCAE ; Set (HL) to point to record count byte in fcb and (DE) to +1992 DCAE ; next record number byte. +1993 DCAE ; +1994 DCAE 2A 43 DB SETHLDE:LD HL,(PARAMS) +1995 DCB1 11 0F 00 LD DE,15 ;record count byte (#15). +1996 DCB4 19 ADD HL,DE +1997 DCB5 EB EX DE,HL +1998 DCB6 21 11 00 LD HL,17 ;next record number (#32). +1999 DCB9 19 ADD HL,DE +2000 DCBA C9 RET +2001 DCBB ; +2002 DCBB ; Save current file data from fcb. +2003 DCBB ; +2004 DCBB CD AE DC STRDATA:CALL SETHLDE +2005 DCBE 7E LD A,(HL) ;get and store record count byte. +2006 DCBF 32 E3 E5 LD (SAVNREC),A +2007 DCC2 EB EX DE,HL +2008 DCC3 7E LD A,(HL) ;get and store next record number byte. +2009 DCC4 32 E1 E5 LD (SAVNXT),A +2010 DCC7 CD A6 DC CALL SETEXT ;point to extent byte. +2011 DCCA 3A C5 E5 LD A,(EXTMASK) ;get extent mask. +2012 DCCD A6 AND (HL) +2013 DCCE 32 E2 E5 LD (SAVEXT),A ;and save extent here. +2014 DCD1 C9 RET +2015 DCD2 ; +2016 DCD2 ; Set the next record to access. If (MODE) is set to 2, then +2017 DCD2 ; the last record byte (SAVNREC) has the correct number to access. +2018 DCD2 ; For sequential access, (MODE) will be equal to 1. +2019 DCD2 ; +2020 DCD2 CD AE DC SETNREC:CALL SETHLDE +2021 DCD5 3A D5 E5 LD A,(MODE) ;get sequential flag (=1). +2022 DCD8 FE 02 CP 2 ;a 2 indicates that no adder is needed. +2023 DCDA C2 DE DC JP NZ,STNREC1 +2024 DCDD AF XOR A ;clear adder (random access?). +2025 DCDE 4F STNREC1:LD C,A +2026 DCDF 3A E3 E5 LD A,(SAVNREC) ;get last record number. +2027 DCE2 81 ADD A,C ;increment record count. +2028 DCE3 77 LD (HL),A ;and set fcb's next record byte. +2029 DCE4 EB EX DE,HL +2030 DCE5 3A E1 E5 LD A,(SAVNXT) ;get next record byte from storage. +2031 DCE8 77 LD (HL),A ;and put this into fcb as number of records used. +2032 DCE9 C9 RET +2033 DCEA ; +2034 DCEA ; Shift (HL) right (C) bits. +2035 DCEA ; +2036 DCEA 0C SHIFTR: INC C +2037 DCEB 0D SHIFTR1:DEC C +2038 DCEC C8 RET Z +2039 DCED 7C LD A,H +2040 DCEE B7 OR A +2041 DCEF 1F RRA +2042 DCF0 67 LD H,A +2043 DCF1 7D LD A,L +2044 DCF2 1F RRA +2045 DCF3 6F LD L,A +2046 DCF4 C3 EB DC JP SHIFTR1 +2047 DCF7 ; +2048 DCF7 ; Compute the check-sum for the directory buffer. Return +2049 DCF7 ; integer sum in (A). +2050 DCF7 ; +2051 DCF7 0E 80 CHECKSUM: LD C,128 ;length of buffer. +2052 DCF9 2A B9 E5 LD HL,(DIRBUF) ;get its location. +2053 DCFC AF XOR A ;clear summation byte. +2054 DCFD 86 CHKSUM1:ADD A,(HL) ;and compute sum ignoring carries. +2055 DCFE 23 INC HL +2056 DCFF 0D DEC C +2057 DD00 C2 FD DC JP NZ,CHKSUM1 +2058 DD03 C9 RET +2059 DD04 ; +2060 DD04 ; Shift (HL) left (C) bits. +2061 DD04 ; +2062 DD04 0C SHIFTL: INC C +2063 DD05 0D SHIFTL1:DEC C +2064 DD06 C8 RET Z +2065 DD07 29 ADD HL,HL ;shift left 1 bit. +2066 DD08 C3 05 DD JP SHIFTL1 +2067 DD0B ; +2068 DD0B ; Routine to set a bit in a 16 bit value contained in (BC). +2069 DD0B ; The bit set depends on the current drive selection. +2070 DD0B ; +2071 DD0B C5 SETBIT: PUSH BC ;save 16 bit word. +2072 DD0C 3A 42 DB LD A,(ACTIVE) ;get active drive. +2073 DD0F 4F LD C,A +2074 DD10 21 01 00 LD HL,1 +2075 DD13 CD 04 DD CALL SHIFTL ;shift bit 0 into place. +2076 DD16 C1 POP BC ;now 'or' this with the original word. +2077 DD17 79 LD A,C +2078 DD18 B5 OR L +2079 DD19 6F LD L,A ;low byte done, do high byte. +2080 DD1A 78 LD A,B +2081 DD1B B4 OR H +2082 DD1C 67 LD H,A +2083 DD1D C9 RET +2084 DD1E ; +2085 DD1E ; Extract the write protect status bit for the current drive. +2086 DD1E ; The result is returned in (A), bit 0. +2087 DD1E ; +2088 DD1E 2A AD E5 GETWPRT:LD HL,(WRTPRT) ;get status bytes. +2089 DD21 3A 42 DB LD A,(ACTIVE) ;which drive is current? +2090 DD24 4F LD C,A +2091 DD25 CD EA DC CALL SHIFTR ;shift status such that bit 0 is the +2092 DD28 7D LD A,L ;one of interest for this drive. +2093 DD29 E6 01 AND 01H ;and isolate it. +2094 DD2B C9 RET +2095 DD2C ; +2096 DD2C ; Function to write protect the current disk. +2097 DD2C ; +2098 DD2C 21 AD E5 WRTPRTD:LD HL,WRTPRT ;point to status word. +2099 DD2F 4E LD C,(HL) ;set (BC) equal to the status. +2100 DD30 23 INC HL +2101 DD31 46 LD B,(HL) +2102 DD32 CD 0B DD CALL SETBIT ;and set this bit according to current drive. +2103 DD35 22 AD E5 LD (WRTPRT),HL ;then save. +2104 DD38 2A C8 E5 LD HL,(DIRSIZE) ;now save directory size limit. +2105 DD3B 23 INC HL ;remember the last one. +2106 DD3C EB EX DE,HL +2107 DD3D 2A B3 E5 LD HL,(SCRATCH1) ;and store it here. +2108 DD40 73 LD (HL),E ;put low byte. +2109 DD41 23 INC HL +2110 DD42 72 LD (HL),D ;then high byte. +2111 DD43 C9 RET +2112 DD44 ; +2113 DD44 ; Check for a read only file. +2114 DD44 ; +2115 DD44 CD 5E DD CHKROFL:CALL FCB2HL ;set (HL) to file entry in directory buffer. +2116 DD47 11 09 00 CKROF1: LD DE,9 ;look at bit 7 of the ninth byte. +2117 DD4A 19 ADD HL,DE +2118 DD4B 7E LD A,(HL) +2119 DD4C 17 RLA +2120 DD4D D0 RET NC ;return if ok. +2121 DD4E 21 0F D8 LD HL,ROFILE ;else, print error message and terminate. +2122 DD51 C3 4A DB JP JUMPHL +2123 DD54 ; +2124 DD54 ; Check the write protect status of the active disk. +2125 DD54 ; +2126 DD54 CD 1E DD CHKWPRT:CALL GETWPRT +2127 DD57 C8 RET Z ;return if ok. +2128 DD58 21 0D D8 LD HL,RODISK ;else print message and terminate. +2129 DD5B C3 4A DB JP JUMPHL +2130 DD5E ; +2131 DD5E ; Routine to set (HL) pointing to the proper entry in the +2132 DD5E ; directory buffer. +2133 DD5E ; +2134 DD5E 2A B9 E5 FCB2HL: LD HL,(DIRBUF) ;get address of buffer. +2135 DD61 3A E9 E5 LD A,(FCBPOS) ;relative position of file. +2136 DD64 ; +2137 DD64 ; Routine to add (A) to (HL). +2138 DD64 ; +2139 DD64 85 ADDA2HL:ADD A,L +2140 DD65 6F LD L,A +2141 DD66 D0 RET NC +2142 DD67 24 INC H ;take care of any carry. +2143 DD68 C9 RET +2144 DD69 ; +2145 DD69 ; Routine to get the 's2' byte from the fcb supplied in +2146 DD69 ; the initial parameter specification. +2147 DD69 ; +2148 DD69 2A 43 DB GETS2: LD HL,(PARAMS) ;get address of fcb. +2149 DD6C 11 0E 00 LD DE,14 ;relative position of 's2'. +2150 DD6F 19 ADD HL,DE +2151 DD70 7E LD A,(HL) ;extract this byte. +2152 DD71 C9 RET +2153 DD72 ; +2154 DD72 ; Clear the 's2' byte in the fcb. +2155 DD72 ; +2156 DD72 CD 69 DD CLEARS2:CALL GETS2 ;this sets (HL) pointing to it. +2157 DD75 36 00 LD (HL),0 ;now clear it. +2158 DD77 C9 RET +2159 DD78 ; +2160 DD78 ; Set bit 7 in the 's2' byte of the fcb. +2161 DD78 ; +2162 DD78 CD 69 DD SETS2B7:CALL GETS2 ;get the byte. +2163 DD7B F6 80 OR 80H ;and set bit 7. +2164 DD7D 77 LD (HL),A ;then store. +2165 DD7E C9 RET +2166 DD7F ; +2167 DD7F ; Compare (FILEPOS) with (SCRATCH1) and set flags based on +2168 DD7F ; the difference. This checks to see if there are more file +2169 DD7F ; names in the directory. We are at (FILEPOS) and there are +2170 DD7F ; (SCRATCH1) of them to check. +2171 DD7F ; +2172 DD7F 2A EA E5 MOREFLS:LD HL,(FILEPOS) ;we are here. +2173 DD82 EB EX DE,HL +2174 DD83 2A B3 E5 LD HL,(SCRATCH1) ;and don't go past here. +2175 DD86 7B LD A,E ;compute difference but don't keep. +2176 DD87 96 SUB (HL) +2177 DD88 23 INC HL +2178 DD89 7A LD A,D +2179 DD8A 9E SBC A,(HL) ;set carry if no more names. +2180 DD8B C9 RET +2181 DD8C ; +2182 DD8C ; Call this routine to prevent (SCRATCH1) from being greater +2183 DD8C ; than (FILEPOS). +2184 DD8C ; +2185 DD8C CD 7F DD CHKNMBR:CALL MOREFLS ;SCRATCH1 too big? +2186 DD8F D8 RET C +2187 DD90 13 INC DE ;yes, reset it to (FILEPOS). +2188 DD91 72 LD (HL),D +2189 DD92 2B DEC HL +2190 DD93 73 LD (HL),E +2191 DD94 C9 RET +2192 DD95 ; +2193 DD95 ; Compute (HL)=(DE)-(HL) +2194 DD95 ; +2195 DD95 7B SUBHL: LD A,E ;compute difference. +2196 DD96 95 SUB L +2197 DD97 6F LD L,A ;store low byte. +2198 DD98 7A LD A,D +2199 DD99 9C SBC A,H +2200 DD9A 67 LD H,A ;and then high byte. +2201 DD9B C9 RET +2202 DD9C ; +2203 DD9C ; Set the directory checksum byte. +2204 DD9C ; +2205 DD9C 0E FF SETDIR: LD C,0FFH +2206 DD9E ; +2207 DD9E ; Routine to set or compare the directory checksum byte. If +2208 DD9E ; (C)=0ffh, then this will set the checksum byte. Else the byte +2209 DD9E ; will be checked. If the check fails (the disk has been changed), +2210 DD9E ; then this disk will be write protected. +2211 DD9E ; +2212 DD9E 2A EC E5 CHECKDIR: LD HL,(CKSUMTBL) +2213 DDA1 EB EX DE,HL +2214 DDA2 2A CC E5 LD HL,(ALLOC1) +2215 DDA5 CD 95 DD CALL SUBHL +2216 DDA8 D0 RET NC ;ok if (CKSUMTBL) > (ALLOC1), so return. +2217 DDA9 C5 PUSH BC +2218 DDAA CD F7 DC CALL CHECKSUM ;else compute checksum. +2219 DDAD 2A BD E5 LD HL,(CHKVECT) ;get address of checksum table. +2220 DDB0 EB EX DE,HL +2221 DDB1 2A EC E5 LD HL,(CKSUMTBL) +2222 DDB4 19 ADD HL,DE ;set (HL) to point to byte for this drive. +2223 DDB5 C1 POP BC +2224 DDB6 0C INC C ;set or check ? +2225 DDB7 CA C4 DD JP Z,CHKDIR1 +2226 DDBA BE CP (HL) ;check them. +2227 DDBB C8 RET Z ;return if they are the same. +2228 DDBC CD 7F DD CALL MOREFLS ;not the same, do we care? +2229 DDBF D0 RET NC +2230 DDC0 CD 2C DD CALL WRTPRTD ;yes, mark this as write protected. +2231 DDC3 C9 RET +2232 DDC4 77 CHKDIR1:LD (HL),A ;just set the byte. +2233 DDC5 C9 RET +2234 DDC6 ; +2235 DDC6 ; Do a write to the directory of the current disk. +2236 DDC6 ; +2237 DDC6 CD 9C DD DIRWRITE: CALL SETDIR ;set checksum byte. +2238 DDC9 CD E0 DD CALL DIRDMA ;set directory dma address. +2239 DDCC 0E 01 LD C,1 ;tell the bios to actually write. +2240 DDCE CD B8 DB CALL DOWRITE ;then do the write. +2241 DDD1 C3 DA DD JP DEFDMA +2242 DDD4 ; +2243 DDD4 ; Read from the directory. +2244 DDD4 ; +2245 DDD4 CD E0 DD DIRREAD:CALL DIRDMA ;set the directory dma address. +2246 DDD7 CD B2 DB CALL DOREAD ;and read it. +2247 DDDA ; +2248 DDDA ; Routine to set the dma address to the users choice. +2249 DDDA ; +2250 DDDA 21 B1 E5 DEFDMA: LD HL,USERDMA ;reset the default dma address and return. +2251 DDDD C3 E3 DD JP DIRDMA1 +2252 DDE0 ; +2253 DDE0 ; Routine to set the dma address for directory work. +2254 DDE0 ; +2255 DDE0 21 B9 E5 DIRDMA: LD HL,DIRBUF +2256 DDE3 ; +2257 DDE3 ; Set the dma address. On entry, (HL) points to +2258 DDE3 ; word containing the desired dma address. +2259 DDE3 ; +2260 DDE3 4E DIRDMA1:LD C,(HL) +2261 DDE4 23 INC HL +2262 DDE5 46 LD B,(HL) ;setup (BC) and go to the bios to set it. +2263 DDE6 C3 24 E6 JP SETDMA +2264 DDE9 ; +2265 DDE9 ; Move the directory buffer into user's dma space. +2266 DDE9 ; +2267 DDE9 2A B9 E5 MOVEDIR:LD HL,(DIRBUF) ;buffer is located here, and +2268 DDEC EB EX DE,HL +2269 DDED 2A B1 E5 LD HL,(USERDMA) ; put it here. +2270 DDF0 0E 80 LD C,128 ;this is its length. +2271 DDF2 C3 4F DB JP DE2HL ;move it now and return. +2272 DDF5 ; +2273 DDF5 ; Check (FILEPOS) and set the zero flag if it equals 0ffffh. +2274 DDF5 ; +2275 DDF5 21 EA E5 CKFILPOS: LD HL,FILEPOS +2276 DDF8 7E LD A,(HL) +2277 DDF9 23 INC HL +2278 DDFA BE CP (HL) ;are both bytes the same? +2279 DDFB C0 RET NZ +2280 DDFC 3C INC A ;yes, but are they each 0ffh? +2281 DDFD C9 RET +2282 DDFE ; +2283 DDFE ; Set location (FILEPOS) to 0ffffh. +2284 DDFE ; +2285 DDFE 21 FF FF STFILPOS: LD HL,0FFFFH +2286 DE01 22 EA E5 LD (FILEPOS),HL +2287 DE04 C9 RET +2288 DE05 ; +2289 DE05 ; Move on to the next file position within the current +2290 DE05 ; directory buffer. If no more exist, set pointer to 0ffffh +2291 DE05 ; and the calling routine will check for this. Enter with (C) +2292 DE05 ; equal to 0ffh to cause the checksum byte to be set, else we +2293 DE05 ; will check this disk and set write protect if checksums are +2294 DE05 ; not the same (applies only if another directory sector must +2295 DE05 ; be read). +2296 DE05 ; +2297 DE05 2A C8 E5 NXENTRY:LD HL,(DIRSIZE) ;get directory entry size limit. +2298 DE08 EB EX DE,HL +2299 DE09 2A EA E5 LD HL,(FILEPOS) ;get current count. +2300 DE0C 23 INC HL ;go on to the next one. +2301 DE0D 22 EA E5 LD (FILEPOS),HL +2302 DE10 CD 95 DD CALL SUBHL ;(HL)=(DIRSIZE)-(FILEPOS) +2303 DE13 D2 19 DE JP NC,NXENT1 ;is there more room left? +2304 DE16 C3 FE DD JP STFILPOS ;no. Set this flag and return. +2305 DE19 3A EA E5 NXENT1: LD A,(FILEPOS) ;get file position within directory. +2306 DE1C E6 03 AND 03H ;only look within this sector (only 4 entries fit). +2307 DE1E 06 05 LD B,5 ;convert to relative position (32 bytes each). +2308 DE20 87 NXENT2: ADD A,A ;note that this is not efficient code. +2309 DE21 05 DEC B ;5 'ADD A's would be better. +2310 DE22 C2 20 DE JP NZ,NXENT2 +2311 DE25 32 E9 E5 LD (FCBPOS),A ;save it as position of fcb. +2312 DE28 B7 OR A +2313 DE29 C0 RET NZ ;return if we are within buffer. +2314 DE2A C5 PUSH BC +2315 DE2B CD C3 DB CALL TRKSEC ;we need the next directory sector. +2316 DE2E CD D4 DD CALL DIRREAD +2317 DE31 C1 POP BC +2318 DE32 C3 9E DD JP CHECKDIR +2319 DE35 ; +2320 DE35 ; Routine to to get a bit from the disk space allocation +2321 DE35 ; map. It is returned in (A), bit position 0. On entry to here, +2322 DE35 ; set (BC) to the block number on the disk to check. +2323 DE35 ; On return, (D) will contain the original bit position for +2324 DE35 ; this block number and (HL) will point to the address for it. +2325 DE35 ; +2326 DE35 79 CKBITMAP: LD A,C ;determine bit number of interest. +2327 DE36 E6 07 AND 07H ;compute (D)=(E)=(C and 7)+1. +2328 DE38 3C INC A +2329 DE39 5F LD E,A ;save particular bit number. +2330 DE3A 57 LD D,A +2331 DE3B ; +2332 DE3B ; compute (BC)=(BC)/8. +2333 DE3B ; +2334 DE3B 79 LD A,C +2335 DE3C 0F RRCA ;now shift right 3 bits. +2336 DE3D 0F RRCA +2337 DE3E 0F RRCA +2338 DE3F E6 1F AND 1FH ;and clear bits 7,6,5. +2339 DE41 4F LD C,A +2340 DE42 78 LD A,B +2341 DE43 87 ADD A,A ;now shift (B) into bits 7,6,5. +2342 DE44 87 ADD A,A +2343 DE45 87 ADD A,A +2344 DE46 87 ADD A,A +2345 DE47 87 ADD A,A +2346 DE48 B1 OR C ;and add in (C). +2347 DE49 4F LD C,A ;ok, (C) ha been completed. +2348 DE4A 78 LD A,B ;is there a better way of doing this? +2349 DE4B 0F RRCA +2350 DE4C 0F RRCA +2351 DE4D 0F RRCA +2352 DE4E E6 1F AND 1FH +2353 DE50 47 LD B,A ;and now (B) is completed. +2354 DE51 ; +2355 DE51 ; use this as an offset into the disk space allocation +2356 DE51 ; table. +2357 DE51 ; +2358 DE51 2A BF E5 LD HL,(ALOCVECT) +2359 DE54 09 ADD HL,BC +2360 DE55 7E LD A,(HL) ;now get correct byte. +2361 DE56 07 CKBMAP1:RLCA ;get correct bit into position 0. +2362 DE57 1D DEC E +2363 DE58 C2 56 DE JP NZ,CKBMAP1 +2364 DE5B C9 RET +2365 DE5C ; +2366 DE5C ; Set or clear the bit map such that block number (BC) will be marked +2367 DE5C ; as used. On entry, if (E)=0 then this bit will be cleared, if it equals +2368 DE5C ; 1 then it will be set (don't use anyother values). +2369 DE5C ; +2370 DE5C D5 STBITMAP: PUSH DE +2371 DE5D CD 35 DE CALL CKBITMAP ;get the byte of interest. +2372 DE60 E6 FE AND 0FEH ;clear the affected bit. +2373 DE62 C1 POP BC +2374 DE63 B1 OR C ;and now set it acording to (C). +2375 DE64 ; +2376 DE64 ; entry to restore the original bit position and then store +2377 DE64 ; in table. (A) contains the value, (D) contains the bit +2378 DE64 ; position (1-8), and (HL) points to the address within the +2379 DE64 ; space allocation table for this byte. +2380 DE64 ; +2381 DE64 0F STBMAP1:RRCA ;restore original bit position. +2382 DE65 15 DEC D +2383 DE66 C2 64 DE JP NZ,STBMAP1 +2384 DE69 77 LD (HL),A ;and stor byte in table. +2385 DE6A C9 RET +2386 DE6B ; +2387 DE6B ; Set/clear space used bits in allocation map for this file. +2388 DE6B ; On entry, (C)=1 to set the map and (C)=0 to clear it. +2389 DE6B ; +2390 DE6B CD 5E DD SETFILE:CALL FCB2HL ;get address of fcb +2391 DE6E 11 10 00 LD DE,16 +2392 DE71 19 ADD HL,DE ;get to block number bytes. +2393 DE72 C5 PUSH BC +2394 DE73 0E 11 LD C,17 ;check all 17 bytes (max) of table. +2395 DE75 D1 SETFL1: POP DE +2396 DE76 0D DEC C ;done all bytes yet? +2397 DE77 C8 RET Z +2398 DE78 D5 PUSH DE +2399 DE79 3A DD E5 LD A,(BIGDISK) ;check disk size for 16 bit block numbers. +2400 DE7C B7 OR A +2401 DE7D CA 88 DE JP Z,SETFL2 +2402 DE80 C5 PUSH BC ;only 8 bit numbers. set (BC) to this one. +2403 DE81 E5 PUSH HL +2404 DE82 4E LD C,(HL) ;get low byte from table, always +2405 DE83 06 00 LD B,0 ;set high byte to zero. +2406 DE85 C3 8E DE JP SETFL3 +2407 DE88 0D SETFL2: DEC C ;for 16 bit block numbers, adjust counter. +2408 DE89 C5 PUSH BC +2409 DE8A 4E LD C,(HL) ;now get both the low and high bytes. +2410 DE8B 23 INC HL +2411 DE8C 46 LD B,(HL) +2412 DE8D E5 PUSH HL +2413 DE8E 79 SETFL3: LD A,C ;block used? +2414 DE8F B0 OR B +2415 DE90 CA 9D DE JP Z,SETFL4 +2416 DE93 2A C6 E5 LD HL,(DSKSIZE) ;is this block number within the +2417 DE96 7D LD A,L ;space on the disk? +2418 DE97 91 SUB C +2419 DE98 7C LD A,H +2420 DE99 98 SBC A,B +2421 DE9A D4 5C DE CALL NC,STBITMAP ;yes, set the proper bit. +2422 DE9D E1 SETFL4: POP HL ;point to next block number in fcb. +2423 DE9E 23 INC HL +2424 DE9F C1 POP BC +2425 DEA0 C3 75 DE JP SETFL1 +2426 DEA3 ; +2427 DEA3 ; Construct the space used allocation bit map for the active +2428 DEA3 ; drive. If a file name starts with '$' and it is under the +2429 DEA3 ; current user number, then (STATUS) is set to minus 1. Otherwise +2430 DEA3 ; it is not set at all. +2431 DEA3 ; +2432 DEA3 2A C6 E5 BITMAP: LD HL,(DSKSIZE) ;compute size of allocation table. +2433 DEA6 0E 03 LD C,3 +2434 DEA8 CD EA DC CALL SHIFTR ;(HL)=(HL)/8. +2435 DEAB 23 INC HL ;at lease 1 byte. +2436 DEAC 44 LD B,H +2437 DEAD 4D LD C,L ;set (BC) to the allocation table length. +2438 DEAE ; +2439 DEAE ; Initialize the bitmap for this drive. Right now, the first +2440 DEAE ; two bytes are specified by the disk parameter block. However +2441 DEAE ; a patch could be entered here if it were necessary to setup +2442 DEAE ; this table in a special mannor. For example, the bios could +2443 DEAE ; determine locations of 'bad blocks' and set them as already +2444 DEAE ; 'used' in the map. +2445 DEAE ; +2446 DEAE 2A BF E5 LD HL,(ALOCVECT) ;now zero out the table now. +2447 DEB1 36 00 BITMAP1:LD (HL),0 +2448 DEB3 23 INC HL +2449 DEB4 0B DEC BC +2450 DEB5 78 LD A,B +2451 DEB6 B1 OR C +2452 DEB7 C2 B1 DE JP NZ,BITMAP1 +2453 DEBA 2A CA E5 LD HL,(ALLOC0) ;get initial space used by directory. +2454 DEBD EB EX DE,HL +2455 DEBE 2A BF E5 LD HL,(ALOCVECT) ;and put this into map. +2456 DEC1 73 LD (HL),E +2457 DEC2 23 INC HL +2458 DEC3 72 LD (HL),D +2459 DEC4 ; +2460 DEC4 ; End of initialization portion. +2461 DEC4 ; +2462 DEC4 CD A1 DB CALL HOMEDRV ;now home the drive. +2463 DEC7 2A B3 E5 LD HL,(SCRATCH1) +2464 DECA 36 03 LD (HL),3 ;force next directory request to read +2465 DECC 23 INC HL ;in a sector. +2466 DECD 36 00 LD (HL),0 +2467 DECF CD FE DD CALL STFILPOS ;clear initial file position also. +2468 DED2 0E FF BITMAP2:LD C,0FFH ;read next file name in directory +2469 DED4 CD 05 DE CALL NXENTRY ;and set checksum byte. +2470 DED7 CD F5 DD CALL CKFILPOS ;is there another file? +2471 DEDA C8 RET Z +2472 DEDB CD 5E DD CALL FCB2HL ;yes, get its address. +2473 DEDE 3E E5 LD A,0E5H +2474 DEE0 BE CP (HL) ;empty file entry? +2475 DEE1 CA D2 DE JP Z,BITMAP2 +2476 DEE4 3A 41 DB LD A,(USERNO) ;no, correct user number? +2477 DEE7 BE CP (HL) +2478 DEE8 C2 F6 DE JP NZ,BITMAP3 +2479 DEEB 23 INC HL +2480 DEEC 7E LD A,(HL) ;yes, does name start with a '$'? +2481 DEED D6 24 SUB '$' +2482 DEEF C2 F6 DE JP NZ,BITMAP3 +2483 DEF2 3D DEC A ;yes, set atatus to minus one. +2484 DEF3 32 45 DB LD (STATUS),A +2485 DEF6 0E 01 BITMAP3:LD C,1 ;now set this file's space as used in bit map. +2486 DEF8 CD 6B DE CALL SETFILE +2487 DEFB CD 8C DD CALL CHKNMBR ;keep (SCRATCH1) in bounds. +2488 DEFE C3 D2 DE JP BITMAP2 +2489 DF01 ; +2490 DF01 ; Set the status (STATUS) and return. +2491 DF01 ; +2492 DF01 3A D4 E5 STSTATUS: LD A,(FNDSTAT) +2493 DF04 C3 01 DB JP SETSTAT +2494 DF07 ; +2495 DF07 ; Check extents in (A) and (C). Set the zero flag if they +2496 DF07 ; are the same. The number of 16k chunks of disk space that +2497 DF07 ; the directory extent covers is expressad is (EXTMASK+1). +2498 DF07 ; No registers are modified. +2499 DF07 ; +2500 DF07 C5 SAMEXT: PUSH BC +2501 DF08 F5 PUSH AF +2502 DF09 3A C5 E5 LD A,(EXTMASK) ;get extent mask and use it to +2503 DF0C 2F CPL ;to compare both extent numbers. +2504 DF0D 47 LD B,A ;save resulting mask here. +2505 DF0E 79 LD A,C ;mask first extent and save in (C). +2506 DF0F A0 AND B +2507 DF10 4F LD C,A +2508 DF11 F1 POP AF ;now mask second extent and compare +2509 DF12 A0 AND B ;with the first one. +2510 DF13 91 SUB C +2511 DF14 E6 1F AND 1FH ;(* only check buts 0-4 *) +2512 DF16 C1 POP BC ;the zero flag is set if they are the same. +2513 DF17 C9 RET ;restore (BC) and return. +2514 DF18 ; +2515 DF18 ; Search for the first occurence of a file name. On entry, +2516 DF18 ; register (C) should contain the number of bytes of the fcb +2517 DF18 ; that must match. +2518 DF18 ; +2519 DF18 3E FF FINDFST:LD A,0FFH +2520 DF1A 32 D4 E5 LD (FNDSTAT),A +2521 DF1D 21 D8 E5 LD HL,COUNTER ;save character count. +2522 DF20 71 LD (HL),C +2523 DF21 2A 43 DB LD HL,(PARAMS) ;get filename to match. +2524 DF24 22 D9 E5 LD (SAVEFCB),HL ;and save. +2525 DF27 CD FE DD CALL STFILPOS ;clear initial file position (set to 0ffffh). +2526 DF2A CD A1 DB CALL HOMEDRV ;home the drive. +2527 DF2D ; +2528 DF2D ; Entry to locate the next occurence of a filename within the +2529 DF2D ; directory. The disk is not expected to have been changed. If +2530 DF2D ; it was, then it will be write protected. +2531 DF2D ; +2532 DF2D 0E 00 FINDNXT:LD C,0 ;write protect the disk if changed. +2533 DF2F CD 05 DE CALL NXENTRY ;get next filename entry in directory. +2534 DF32 CD F5 DD CALL CKFILPOS ;is file position = 0ffffh? +2535 DF35 CA 94 DF JP Z,FNDNXT6 ;yes, exit now then. +2536 DF38 2A D9 E5 LD HL,(SAVEFCB) ;set (DE) pointing to filename to match. +2537 DF3B EB EX DE,HL +2538 DF3C 1A LD A,(DE) +2539 DF3D FE E5 CP 0E5H ;empty directory entry? +2540 DF3F CA 4A DF JP Z,FNDNXT1 ;(* are we trying to reserect erased entries? *) +2541 DF42 D5 PUSH DE +2542 DF43 CD 7F DD CALL MOREFLS ;more files in directory? +2543 DF46 D1 POP DE +2544 DF47 D2 94 DF JP NC,FNDNXT6 ;no more. Exit now. +2545 DF4A CD 5E DD FNDNXT1:CALL FCB2HL ;get address of this fcb in directory. +2546 DF4D 3A D8 E5 LD A,(COUNTER) ;get number of bytes (characters) to check. +2547 DF50 4F LD C,A +2548 DF51 06 00 LD B,0 ;initialize byte position counter. +2549 DF53 79 FNDNXT2:LD A,C ;are we done with the compare? +2550 DF54 B7 OR A +2551 DF55 CA 83 DF JP Z,FNDNXT5 +2552 DF58 1A LD A,(DE) ;no, check next byte. +2553 DF59 FE 3F CP '?' ;don't care about this character? +2554 DF5B CA 7C DF JP Z,FNDNXT4 +2555 DF5E 78 LD A,B ;get bytes position in fcb. +2556 DF5F FE 0D CP 13 ;don't care about the thirteenth byte either. +2557 DF61 CA 7C DF JP Z,FNDNXT4 +2558 DF64 FE 0C CP 12 ;extent byte? +2559 DF66 1A LD A,(DE) +2560 DF67 CA 73 DF JP Z,FNDNXT3 +2561 DF6A 96 SUB (HL) ;otherwise compare characters. +2562 DF6B E6 7F AND 7FH +2563 DF6D C2 2D DF JP NZ,FINDNXT ;not the same, check next entry. +2564 DF70 C3 7C DF JP FNDNXT4 ;so far so good, keep checking. +2565 DF73 C5 FNDNXT3:PUSH BC ;check the extent byte here. +2566 DF74 4E LD C,(HL) +2567 DF75 CD 07 DF CALL SAMEXT +2568 DF78 C1 POP BC +2569 DF79 C2 2D DF JP NZ,FINDNXT ;not the same, look some more. +2570 DF7C ; +2571 DF7C ; So far the names compare. Bump pointers to the next byte +2572 DF7C ; and continue until all (C) characters have been checked. +2573 DF7C ; +2574 DF7C 13 FNDNXT4:INC DE ;bump pointers. +2575 DF7D 23 INC HL +2576 DF7E 04 INC B +2577 DF7F 0D DEC C ;adjust character counter. +2578 DF80 C3 53 DF JP FNDNXT2 +2579 DF83 3A EA E5 FNDNXT5:LD A,(FILEPOS) ;return the position of this entry. +2580 DF86 E6 03 AND 03H +2581 DF88 32 45 DB LD (STATUS),A +2582 DF8B 21 D4 E5 LD HL,FNDSTAT +2583 DF8E 7E LD A,(HL) +2584 DF8F 17 RLA +2585 DF90 D0 RET NC +2586 DF91 AF XOR A +2587 DF92 77 LD (HL),A +2588 DF93 C9 RET +2589 DF94 ; +2590 DF94 ; Filename was not found. Set appropriate status. +2591 DF94 ; +2592 DF94 CD FE DD FNDNXT6:CALL STFILPOS ;set (FILEPOS) to 0ffffh. +2593 DF97 3E FF LD A,0FFH ;say not located. +2594 DF99 C3 01 DB JP SETSTAT +2595 DF9C ; +2596 DF9C ; Erase files from the directory. Only the first byte of the +2597 DF9C ; fcb will be affected. It is set to (E5). +2598 DF9C ; +2599 DF9C CD 54 DD ERAFILE:CALL CHKWPRT ;is disk write protected? +2600 DF9F 0E 0C LD C,12 ;only compare file names. +2601 DFA1 CD 18 DF CALL FINDFST ;get first file name. +2602 DFA4 CD F5 DD ERAFIL1:CALL CKFILPOS ;any found? +2603 DFA7 C8 RET Z ;nope, we must be done. +2604 DFA8 CD 44 DD CALL CHKROFL ;is file read only? +2605 DFAB CD 5E DD CALL FCB2HL ;nope, get address of fcb and +2606 DFAE 36 E5 LD (HL),0E5H ;set first byte to 'empty'. +2607 DFB0 0E 00 LD C,0 ;clear the space from the bit map. +2608 DFB2 CD 6B DE CALL SETFILE +2609 DFB5 CD C6 DD CALL DIRWRITE ;now write the directory sector back out. +2610 DFB8 CD 2D DF CALL FINDNXT ;find the next file name. +2611 DFBB C3 A4 DF JP ERAFIL1 ;and repeat process. +2612 DFBE ; +2613 DFBE ; Look through the space allocation map (bit map) for the +2614 DFBE ; next available block. Start searching at block number (BC-1). +2615 DFBE ; The search procedure is to look for an empty block that is +2616 DFBE ; before the starting block. If not empty, look at a later +2617 DFBE ; block number. In this way, we return the closest empty block +2618 DFBE ; on either side of the 'target' block number. This will speed +2619 DFBE ; access on random devices. For serial devices, this should be +2620 DFBE ; changed to look in the forward direction first and then start +2621 DFBE ; at the front and search some more. +2622 DFBE ; +2623 DFBE ; On return, (DE)= block number that is empty and (HL) =0 +2624 DFBE ; if no empry block was found. +2625 DFBE ; +2626 DFBE 50 FNDSPACE: LD D,B ;set (DE) as the block that is checked. +2627 DFBF 59 LD E,C +2628 DFC0 ; +2629 DFC0 ; Look before target block. Registers (BC) are used as the lower +2630 DFC0 ; pointer and (DE) as the upper pointer. +2631 DFC0 ; +2632 DFC0 79 FNDSPA1:LD A,C ;is block 0 specified? +2633 DFC1 B0 OR B +2634 DFC2 CA D1 DF JP Z,FNDSPA2 +2635 DFC5 0B DEC BC ;nope, check previous block. +2636 DFC6 D5 PUSH DE +2637 DFC7 C5 PUSH BC +2638 DFC8 CD 35 DE CALL CKBITMAP +2639 DFCB 1F RRA ;is this block empty? +2640 DFCC D2 EC DF JP NC,FNDSPA3 ;yes. use this. +2641 DFCF ; +2642 DFCF ; Note that the above logic gets the first block that it finds +2643 DFCF ; that is empty. Thus a file could be written 'backward' making +2644 DFCF ; it very slow to access. This could be changed to look for the +2645 DFCF ; first empty block and then continue until the start of this +2646 DFCF ; empty space is located and then used that starting block. +2647 DFCF ; This should help speed up access to some files especially on +2648 DFCF ; a well used disk with lots of fairly small 'holes'. +2649 DFCF ; +2650 DFCF C1 POP BC ;nope, check some more. +2651 DFD0 D1 POP DE +2652 DFD1 ; +2653 DFD1 ; Now look after target block. +2654 DFD1 ; +2655 DFD1 2A C6 E5 FNDSPA2:LD HL,(DSKSIZE) ;is block (DE) within disk limits? +2656 DFD4 7B LD A,E +2657 DFD5 95 SUB L +2658 DFD6 7A LD A,D +2659 DFD7 9C SBC A,H +2660 DFD8 D2 F4 DF JP NC,FNDSPA4 +2661 DFDB 13 INC DE ;yes, move on to next one. +2662 DFDC C5 PUSH BC +2663 DFDD D5 PUSH DE +2664 DFDE 42 LD B,D +2665 DFDF 4B LD C,E +2666 DFE0 CD 35 DE CALL CKBITMAP ;check it. +2667 DFE3 1F RRA ;empty? +2668 DFE4 D2 EC DF JP NC,FNDSPA3 +2669 DFE7 D1 POP DE ;nope, continue searching. +2670 DFE8 C1 POP BC +2671 DFE9 C3 C0 DF JP FNDSPA1 +2672 DFEC ; +2673 DFEC ; Empty block found. Set it as used and return with (HL) +2674 DFEC ; pointing to it (true?). +2675 DFEC ; +2676 DFEC 17 FNDSPA3:RLA ;reset byte. +2677 DFED 3C INC A ;and set bit 0. +2678 DFEE CD 64 DE CALL STBMAP1 ;update bit map. +2679 DFF1 E1 POP HL ;set return registers. +2680 DFF2 D1 POP DE +2681 DFF3 C9 RET +2682 DFF4 ; +2683 DFF4 ; Free block was not found. If (BC) is not zero, then we have +2684 DFF4 ; not checked all of the disk space. +2685 DFF4 ; +2686 DFF4 79 FNDSPA4:LD A,C +2687 DFF5 B0 OR B +2688 DFF6 C2 C0 DF JP NZ,FNDSPA1 +2689 DFF9 21 00 00 LD HL,0 ;set 'not found' status. +2690 DFFC C9 RET +2691 DFFD ; +2692 DFFD ; Move a complete fcb entry into the directory and write it. +2693 DFFD ; +2694 DFFD 0E 00 FCBSET: LD C,0 +2695 DFFF 1E 20 LD E,32 ;length of each entry. +2696 E001 ; +2697 E001 ; Move (E) bytes from the fcb pointed to by (PARAMS) into +2698 E001 ; fcb in directory starting at relative byte (C). This updated +2699 E001 ; directory buffer is then written to the disk. +2700 E001 ; +2701 E001 D5 UPDATE: PUSH DE +2702 E002 06 00 LD B,0 ;set (BC) to relative byte position. +2703 E004 2A 43 DB LD HL,(PARAMS) ;get address of fcb. +2704 E007 09 ADD HL,BC ;compute starting byte. +2705 E008 EB EX DE,HL +2706 E009 CD 5E DD CALL FCB2HL ;get address of fcb to update in directory. +2707 E00C C1 POP BC ;set (C) to number of bytes to change. +2708 E00D CD 4F DB CALL DE2HL +2709 E010 CD C3 DB UPDATE1:CALL TRKSEC ;determine the track and sector affected. +2710 E013 C3 C6 DD JP DIRWRITE ;then write this sector out. +2711 E016 ; +2712 E016 ; Routine to change the name of all files on the disk with a +2713 E016 ; specified name. The fcb contains the current name as the +2714 E016 ; first 12 characters and the new name 16 bytes into the fcb. +2715 E016 ; +2716 E016 CD 54 DD CHGNAMES: CALL CHKWPRT ;check for a write protected disk. +2717 E019 0E 0C LD C,12 ;match first 12 bytes of fcb only. +2718 E01B CD 18 DF CALL FINDFST ;get first name. +2719 E01E 2A 43 DB LD HL,(PARAMS) ;get address of fcb. +2720 E021 7E LD A,(HL) ;get user number. +2721 E022 11 10 00 LD DE,16 ;move over to desired name. +2722 E025 19 ADD HL,DE +2723 E026 77 LD (HL),A ;keep same user number. +2724 E027 CD F5 DD CHGNAM1:CALL CKFILPOS ;any matching file found? +2725 E02A C8 RET Z ;no, we must be done. +2726 E02B CD 44 DD CALL CHKROFL ;check for read only file. +2727 E02E 0E 10 LD C,16 ;start 16 bytes into fcb. +2728 E030 1E 0C LD E,12 ;and update the first 12 bytes of directory. +2729 E032 CD 01 E0 CALL UPDATE +2730 E035 CD 2D DF CALL FINDNXT ;get te next file name. +2731 E038 C3 27 E0 JP CHGNAM1 ;and continue. +2732 E03B ; +2733 E03B ; Update a files attributes. The procedure is to search for +2734 E03B ; every file with the same name as shown in fcb (ignoring bit 7) +2735 E03B ; and then to update it (which includes bit 7). No other changes +2736 E03B ; are made. +2737 E03B ; +2738 E03B 0E 0C SAVEATTR: LD C,12 ;match first 12 bytes. +2739 E03D CD 18 DF CALL FINDFST ;look for first filename. +2740 E040 CD F5 DD SAVATR1:CALL CKFILPOS ;was one found? +2741 E043 C8 RET Z ;nope, we must be done. +2742 E044 0E 00 LD C,0 ;yes, update the first 12 bytes now. +2743 E046 1E 0C LD E,12 +2744 E048 CD 01 E0 CALL UPDATE ;update filename and write directory. +2745 E04B CD 2D DF CALL FINDNXT ;and get the next file. +2746 E04E C3 40 E0 JP SAVATR1 ;then continue until done. +2747 E051 ; +2748 E051 ; Open a file (name specified in fcb). +2749 E051 ; +2750 E051 0E 0F OPENIT: LD C,15 ;compare the first 15 bytes. +2751 E053 CD 18 DF CALL FINDFST ;get the first one in directory. +2752 E056 CD F5 DD CALL CKFILPOS ;any at all? +2753 E059 C8 RET Z +2754 E05A CD A6 DC OPENIT1:CALL SETEXT ;point to extent byte within users fcb. +2755 E05D 7E LD A,(HL) ;and get it. +2756 E05E F5 PUSH AF ;save it and address. +2757 E05F E5 PUSH HL +2758 E060 CD 5E DD CALL FCB2HL ;point to fcb in directory. +2759 E063 EB EX DE,HL +2760 E064 2A 43 DB LD HL,(PARAMS) ;this is the users copy. +2761 E067 0E 20 LD C,32 ;move it into users space. +2762 E069 D5 PUSH DE +2763 E06A CD 4F DB CALL DE2HL +2764 E06D CD 78 DD CALL SETS2B7 ;set bit 7 in 's2' byte (unmodified). +2765 E070 D1 POP DE ;now get the extent byte from this fcb. +2766 E071 21 0C 00 LD HL,12 +2767 E074 19 ADD HL,DE +2768 E075 4E LD C,(HL) ;into (C). +2769 E076 21 0F 00 LD HL,15 ;now get the record count byte into (B). +2770 E079 19 ADD HL,DE +2771 E07A 46 LD B,(HL) +2772 E07B E1 POP HL ;keep the same extent as the user had originally. +2773 E07C F1 POP AF +2774 E07D 77 LD (HL),A +2775 E07E 79 LD A,C ;is it the same as in the directory fcb? +2776 E07F BE CP (HL) +2777 E080 78 LD A,B ;if yes, then use the same record count. +2778 E081 CA 8B E0 JP Z,OPENIT2 +2779 E084 3E 00 LD A,0 ;if the user specified an extent greater than +2780 E086 DA 8B E0 JP C,OPENIT2 ;the one in the directory, then set record count to 0. +2781 E089 3E 80 LD A,128 ;otherwise set to maximum. +2782 E08B 2A 43 DB OPENIT2:LD HL,(PARAMS) ;set record count in users fcb to (A). +2783 E08E 11 0F 00 LD DE,15 +2784 E091 19 ADD HL,DE ;compute relative position. +2785 E092 77 LD (HL),A ;and set the record count. +2786 E093 C9 RET +2787 E094 ; +2788 E094 ; Move two bytes from (DE) to (HL) if (and only if) (HL) +2789 E094 ; point to a zero value (16 bit). +2790 E094 ; Return with zero flag set it (DE) was moved. Registers (DE) +2791 E094 ; and (HL) are not changed. However (A) is. +2792 E094 ; +2793 E094 7E MOVEWORD: LD A,(HL) ;check for a zero word. +2794 E095 23 INC HL +2795 E096 B6 OR (HL) ;both bytes zero? +2796 E097 2B DEC HL +2797 E098 C0 RET NZ ;nope, just return. +2798 E099 1A LD A,(DE) ;yes, move two bytes from (DE) into +2799 E09A 77 LD (HL),A ;this zero space. +2800 E09B 13 INC DE +2801 E09C 23 INC HL +2802 E09D 1A LD A,(DE) +2803 E09E 77 LD (HL),A +2804 E09F 1B DEC DE ;don't disturb these registers. +2805 E0A0 2B DEC HL +2806 E0A1 C9 RET +2807 E0A2 ; +2808 E0A2 ; Get here to close a file specified by (fcb). +2809 E0A2 ; +2810 E0A2 AF CLOSEIT:XOR A ;clear status and file position bytes. +2811 E0A3 32 45 DB LD (STATUS),A +2812 E0A6 32 EA E5 LD (FILEPOS),A +2813 E0A9 32 EB E5 LD (FILEPOS+1),A +2814 E0AC CD 1E DD CALL GETWPRT ;get write protect bit for this drive. +2815 E0AF C0 RET NZ ;just return if it is set. +2816 E0B0 CD 69 DD CALL GETS2 ;else get the 's2' byte. +2817 E0B3 E6 80 AND 80H ;and look at bit 7 (file unmodified?). +2818 E0B5 C0 RET NZ ;just return if set. +2819 E0B6 0E 0F LD C,15 ;else look up this file in directory. +2820 E0B8 CD 18 DF CALL FINDFST +2821 E0BB CD F5 DD CALL CKFILPOS ;was it found? +2822 E0BE C8 RET Z ;just return if not. +2823 E0BF 01 10 00 LD BC,16 ;set (HL) pointing to records used section. +2824 E0C2 CD 5E DD CALL FCB2HL +2825 E0C5 09 ADD HL,BC +2826 E0C6 EB EX DE,HL +2827 E0C7 2A 43 DB LD HL,(PARAMS) ;do the same for users specified fcb. +2828 E0CA 09 ADD HL,BC +2829 E0CB 0E 10 LD C,16 ;this many bytes are present in this extent. +2830 E0CD 3A DD E5 CLOSEIT1: LD A,(BIGDISK) ;8 or 16 bit record numbers? +2831 E0D0 B7 OR A +2832 E0D1 CA E8 E0 JP Z,CLOSEIT4 +2833 E0D4 7E LD A,(HL) ;just 8 bit. Get one from users fcb. +2834 E0D5 B7 OR A +2835 E0D6 1A LD A,(DE) ;now get one from directory fcb. +2836 E0D7 C2 DB E0 JP NZ,CLOSEIT2 +2837 E0DA 77 LD (HL),A ;users byte was zero. Update from directory. +2838 E0DB B7 CLOSEIT2: OR A +2839 E0DC C2 E1 E0 JP NZ,CLOSEIT3 +2840 E0DF 7E LD A,(HL) ;directories byte was zero, update from users fcb. +2841 E0E0 12 LD (DE),A +2842 E0E1 BE CLOSEIT3: CP (HL) ;if neither one of these bytes were zero, +2843 E0E2 C2 1F E1 JP NZ,CLOSEIT7 ;then close error if they are not the same. +2844 E0E5 C3 FD E0 JP CLOSEIT5 ;ok so far, get to next byte in fcbs. +2845 E0E8 CD 94 E0 CLOSEIT4: CALL MOVEWORD ;update users fcb if it is zero. +2846 E0EB EB EX DE,HL +2847 E0EC CD 94 E0 CALL MOVEWORD ;update directories fcb if it is zero. +2848 E0EF EB EX DE,HL +2849 E0F0 1A LD A,(DE) ;if these two values are no different, +2850 E0F1 BE CP (HL) ;then a close error occured. +2851 E0F2 C2 1F E1 JP NZ,CLOSEIT7 +2852 E0F5 13 INC DE ;check second byte. +2853 E0F6 23 INC HL +2854 E0F7 1A LD A,(DE) +2855 E0F8 BE CP (HL) +2856 E0F9 C2 1F E1 JP NZ,CLOSEIT7 +2857 E0FC 0D DEC C ;remember 16 bit values. +2858 E0FD 13 CLOSEIT5: INC DE ;bump to next item in table. +2859 E0FE 23 INC HL +2860 E0FF 0D DEC C ;there are 16 entries only. +2861 E100 C2 CD E0 JP NZ,CLOSEIT1 ;continue if more to do. +2862 E103 01 EC FF LD BC,0FFECH ;backup 20 places (extent byte). +2863 E106 09 ADD HL,BC +2864 E107 EB EX DE,HL +2865 E108 09 ADD HL,BC +2866 E109 1A LD A,(DE) +2867 E10A BE CP (HL) ;directory's extent already greater than the +2868 E10B DA 17 E1 JP C,CLOSEIT6 ;users extent? +2869 E10E 77 LD (HL),A ;no, update directory extent. +2870 E10F 01 03 00 LD BC,3 ;and update the record count byte in +2871 E112 09 ADD HL,BC ;directories fcb. +2872 E113 EB EX DE,HL +2873 E114 09 ADD HL,BC +2874 E115 7E LD A,(HL) ;get from user. +2875 E116 12 LD (DE),A ;and put in directory. +2876 E117 3E FF CLOSEIT6: LD A,0FFH ;set 'was open and is now closed' byte. +2877 E119 32 D2 E5 LD (CLOSEFLG),A +2878 E11C C3 10 E0 JP UPDATE1 ;update the directory now. +2879 E11F 21 45 DB CLOSEIT7: LD HL,STATUS ;set return status and then return. +2880 E122 35 DEC (HL) +2881 E123 C9 RET +2882 E124 ; +2883 E124 ; Routine to get the next empty space in the directory. It +2884 E124 ; will then be cleared for use. +2885 E124 ; +2886 E124 CD 54 DD GETEMPTY: CALL CHKWPRT ;make sure disk is not write protected. +2887 E127 2A 43 DB LD HL,(PARAMS) ;save current parameters (fcb). +2888 E12A E5 PUSH HL +2889 E12B 21 AC E5 LD HL,EMPTYFCB ;use special one for empty space. +2890 E12E 22 43 DB LD (PARAMS),HL +2891 E131 0E 01 LD C,1 ;search for first empty spot in directory. +2892 E133 CD 18 DF CALL FINDFST ;(* only check first byte *) +2893 E136 CD F5 DD CALL CKFILPOS ;none? +2894 E139 E1 POP HL +2895 E13A 22 43 DB LD (PARAMS),HL ;restore original fcb address. +2896 E13D C8 RET Z ;return if no more space. +2897 E13E EB EX DE,HL +2898 E13F 21 0F 00 LD HL,15 ;point to number of records for this file. +2899 E142 19 ADD HL,DE +2900 E143 0E 11 LD C,17 ;and clear all of this space. +2901 E145 AF XOR A +2902 E146 77 GETMT1: LD (HL),A +2903 E147 23 INC HL +2904 E148 0D DEC C +2905 E149 C2 46 E1 JP NZ,GETMT1 +2906 E14C 21 0D 00 LD HL,13 ;clear the 's1' byte also. +2907 E14F 19 ADD HL,DE +2908 E150 77 LD (HL),A +2909 E151 CD 8C DD CALL CHKNMBR ;keep (SCRATCH1) within bounds. +2910 E154 CD FD DF CALL FCBSET ;write out this fcb entry to directory. +2911 E157 C3 78 DD JP SETS2B7 ;set 's2' byte bit 7 (unmodified at present). +2912 E15A ; +2913 E15A ; Routine to close the current extent and open the next one +2914 E15A ; for reading. +2915 E15A ; +2916 E15A AF GETNEXT:XOR A +2917 E15B 32 D2 E5 LD (CLOSEFLG),A ;clear close flag. +2918 E15E CD A2 E0 CALL CLOSEIT ;close this extent. +2919 E161 CD F5 DD CALL CKFILPOS +2920 E164 C8 RET Z ;not there??? +2921 E165 2A 43 DB LD HL,(PARAMS) ;get extent byte. +2922 E168 01 0C 00 LD BC,12 +2923 E16B 09 ADD HL,BC +2924 E16C 7E LD A,(HL) ;and increment it. +2925 E16D 3C INC A +2926 E16E E6 1F AND 1FH ;keep within range 0-31. +2927 E170 77 LD (HL),A +2928 E171 CA 83 E1 JP Z,GTNEXT1 ;overflow? +2929 E174 47 LD B,A ;mask extent byte. +2930 E175 3A C5 E5 LD A,(EXTMASK) +2931 E178 A0 AND B +2932 E179 21 D2 E5 LD HL,CLOSEFLG ;check close flag (0ffh is ok). +2933 E17C A6 AND (HL) +2934 E17D CA 8E E1 JP Z,GTNEXT2 ;if zero, we must read in next extent. +2935 E180 C3 AC E1 JP GTNEXT3 ;else, it is already in memory. +2936 E183 01 02 00 GTNEXT1:LD BC,2 ;Point to the 's2' byte. +2937 E186 09 ADD HL,BC +2938 E187 34 INC (HL) ;and bump it. +2939 E188 7E LD A,(HL) ;too many extents? +2940 E189 E6 0F AND 0FH +2941 E18B CA B6 E1 JP Z,GTNEXT5 ;yes, set error code. +2942 E18E ; +2943 E18E ; Get here to open the next extent. +2944 E18E ; +2945 E18E 0E 0F GTNEXT2:LD C,15 ;set to check first 15 bytes of fcb. +2946 E190 CD 18 DF CALL FINDFST ;find the first one. +2947 E193 CD F5 DD CALL CKFILPOS ;none available? +2948 E196 C2 AC E1 JP NZ,GTNEXT3 +2949 E199 3A D3 E5 LD A,(RDWRTFLG) ;no extent present. Can we open an empty one? +2950 E19C 3C INC A ;0ffh means reading (so not possible). +2951 E19D CA B6 E1 JP Z,GTNEXT5 ;or an error. +2952 E1A0 CD 24 E1 CALL GETEMPTY ;we are writing, get an empty entry. +2953 E1A3 CD F5 DD CALL CKFILPOS ;none? +2954 E1A6 CA B6 E1 JP Z,GTNEXT5 ;error if true. +2955 E1A9 C3 AF E1 JP GTNEXT4 ;else we are almost done. +2956 E1AC CD 5A E0 GTNEXT3:CALL OPENIT1 ;open this extent. +2957 E1AF CD BB DC GTNEXT4:CALL STRDATA ;move in updated data (rec #, extent #, etc.) +2958 E1B2 AF XOR A ;clear status and return. +2959 E1B3 C3 01 DB JP SETSTAT +2960 E1B6 ; +2961 E1B6 ; Error in extending the file. Too many extents were needed +2962 E1B6 ; or not enough space on the disk. +2963 E1B6 ; +2964 E1B6 CD 05 DB GTNEXT5:CALL IOERR1 ;set error code, clear bit 7 of 's2' +2965 E1B9 C3 78 DD JP SETS2B7 ;so this is not written on a close. +2966 E1BC ; +2967 E1BC ; Read a sequential file. +2968 E1BC ; +2969 E1BC 3E 01 RDSEQ: LD A,1 ;set sequential access mode. +2970 E1BE 32 D5 E5 LD (MODE),A +2971 E1C1 3E FF RDSEQ1: LD A,0FFH ;don't allow reading unwritten space. +2972 E1C3 32 D3 E5 LD (RDWRTFLG),A +2973 E1C6 CD BB DC CALL STRDATA ;put rec# and ext# into fcb. +2974 E1C9 3A E3 E5 LD A,(SAVNREC) ;get next record to read. +2975 E1CC 21 E1 E5 LD HL,SAVNXT ;get number of records in extent. +2976 E1CF BE CP (HL) ;within this extent? +2977 E1D0 DA E6 E1 JP C,RDSEQ2 +2978 E1D3 FE 80 CP 128 ;no. Is this extent fully used? +2979 E1D5 C2 FB E1 JP NZ,RDSEQ3 ;no. End-of-file. +2980 E1D8 CD 5A E1 CALL GETNEXT ;yes, open the next one. +2981 E1DB AF XOR A ;reset next record to read. +2982 E1DC 32 E3 E5 LD (SAVNREC),A +2983 E1DF 3A 45 DB LD A,(STATUS) ;check on open, successful? +2984 E1E2 B7 OR A +2985 E1E3 C2 FB E1 JP NZ,RDSEQ3 ;no, error. +2986 E1E6 CD 77 DC RDSEQ2: CALL COMBLK ;ok. compute block number to read. +2987 E1E9 CD 84 DC CALL CHKBLK ;check it. Within bounds? +2988 E1EC CA FB E1 JP Z,RDSEQ3 ;no, error. +2989 E1EF CD 8A DC CALL LOGICAL ;convert (BLKNMBR) to logical sector (128 byte). +2990 E1F2 CD D1 DB CALL TRKSEC1 ;set the track and sector for this block #. +2991 E1F5 CD B2 DB CALL DOREAD ;and read it. +2992 E1F8 C3 D2 DC JP SETNREC ;and set the next record to be accessed. +2993 E1FB ; +2994 E1FB ; Read error occured. Set status and return. +2995 E1FB ; +2996 E1FB C3 05 DB RDSEQ3: JP IOERR1 +2997 E1FE ; +2998 E1FE ; Write the next sequential record. +2999 E1FE ; +3000 E1FE 3E 01 WTSEQ: LD A,1 ;set sequential access mode. +3001 E200 32 D5 E5 LD (MODE),A +3002 E203 3E 00 WTSEQ1: LD A,0 ;allow an addition empty extent to be opened. +3003 E205 32 D3 E5 LD (RDWRTFLG),A +3004 E208 CD 54 DD CALL CHKWPRT ;check write protect status. +3005 E20B 2A 43 DB LD HL,(PARAMS) +3006 E20E CD 47 DD CALL CKROF1 ;check for read only file, (HL) already set to fcb. +3007 E211 CD BB DC CALL STRDATA ;put updated data into fcb. +3008 E214 3A E3 E5 LD A,(SAVNREC) ;get record number to write. +3009 E217 FE 80 CP 128 ;within range? +3010 E219 D2 05 DB JP NC,IOERR1 ;no, error(?). +3011 E21C CD 77 DC CALL COMBLK ;compute block number. +3012 E21F CD 84 DC CALL CHKBLK ;check number. +3013 E222 0E 00 LD C,0 ;is there one to write to? +3014 E224 C2 6E E2 JP NZ,WTSEQ6 ;yes, go do it. +3015 E227 CD 3E DC CALL GETBLOCK ;get next block number within fcb to use. +3016 E22A 32 D7 E5 LD (RELBLOCK),A ;and save. +3017 E22D 01 00 00 LD BC,0 ;start looking for space from the start +3018 E230 B7 OR A ;if none allocated as yet. +3019 E231 CA 3B E2 JP Z,WTSEQ2 +3020 E234 4F LD C,A ;extract previous block number from fcb +3021 E235 0B DEC BC ;so we can be closest to it. +3022 E236 CD 5E DC CALL EXTBLK +3023 E239 44 LD B,H +3024 E23A 4D LD C,L +3025 E23B CD BE DF WTSEQ2: CALL FNDSPACE ;find the next empty block nearest number (BC). +3026 E23E 7D LD A,L ;check for a zero number. +3027 E23F B4 OR H +3028 E240 C2 48 E2 JP NZ,WTSEQ3 +3029 E243 3E 02 LD A,2 ;no more space? +3030 E245 C3 01 DB JP SETSTAT +3031 E248 22 E5 E5 WTSEQ3: LD (BLKNMBR),HL ;save block number to access. +3032 E24B EB EX DE,HL ;put block number into (DE). +3033 E24C 2A 43 DB LD HL,(PARAMS) ;now we must update the fcb for this +3034 E24F 01 10 00 LD BC,16 ;newly allocated block. +3035 E252 09 ADD HL,BC +3036 E253 3A DD E5 LD A,(BIGDISK) ;8 or 16 bit block numbers? +3037 E256 B7 OR A +3038 E257 3A D7 E5 LD A,(RELBLOCK) ;(* update this entry *) +3039 E25A CA 64 E2 JP Z,WTSEQ4 ;zero means 16 bit ones. +3040 E25D CD 64 DD CALL ADDA2HL ;(HL)=(HL)+(A) +3041 E260 73 LD (HL),E ;store new block number. +3042 E261 C3 6C E2 JP WTSEQ5 +3043 E264 4F WTSEQ4: LD C,A ;compute spot in this 16 bit table. +3044 E265 06 00 LD B,0 +3045 E267 09 ADD HL,BC +3046 E268 09 ADD HL,BC +3047 E269 73 LD (HL),E ;stuff block number (DE) there. +3048 E26A 23 INC HL +3049 E26B 72 LD (HL),D +3050 E26C 0E 02 WTSEQ5: LD C,2 ;set (C) to indicate writing to un-used disk space. +3051 E26E 3A 45 DB WTSEQ6: LD A,(STATUS) ;are we ok so far? +3052 E271 B7 OR A +3053 E272 C0 RET NZ +3054 E273 C5 PUSH BC ;yes, save write flag for bios (register C). +3055 E274 CD 8A DC CALL LOGICAL ;convert (BLKNMBR) over to loical sectors. +3056 E277 3A D5 E5 LD A,(MODE) ;get access mode flag (1=sequential, +3057 E27A 3D DEC A ;0=random, 2=special?). +3058 E27B 3D DEC A +3059 E27C C2 BB E2 JP NZ,WTSEQ9 +3060 E27F ; +3061 E27F ; Special random i/o from function #40. Maybe for M/PM, but the +3062 E27F ; current block, if it has not been written to, will be zeroed +3063 E27F ; out and then written (reason?). +3064 E27F ; +3065 E27F C1 POP BC +3066 E280 C5 PUSH BC +3067 E281 79 LD A,C ;get write status flag (2=writing unused space). +3068 E282 3D DEC A +3069 E283 3D DEC A +3070 E284 C2 BB E2 JP NZ,WTSEQ9 +3071 E287 E5 PUSH HL +3072 E288 2A B9 E5 LD HL,(DIRBUF) ;zero out the directory buffer. +3073 E28B 57 LD D,A ;note that (A) is zero here. +3074 E28C 77 WTSEQ7: LD (HL),A +3075 E28D 23 INC HL +3076 E28E 14 INC D ;do 128 bytes. +3077 E28F F2 8C E2 JP P,WTSEQ7 +3078 E292 CD E0 DD CALL DIRDMA ;tell the bios the dma address for directory access. +3079 E295 2A E7 E5 LD HL,(LOGSECT) ;get sector that starts current block. +3080 E298 0E 02 LD C,2 ;set 'writing to unused space' flag. +3081 E29A 22 E5 E5 WTSEQ8: LD (BLKNMBR),HL ;save sector to write. +3082 E29D C5 PUSH BC +3083 E29E CD D1 DB CALL TRKSEC1 ;determine its track and sector numbers. +3084 E2A1 C1 POP BC +3085 E2A2 CD B8 DB CALL DOWRITE ;now write out 128 bytes of zeros. +3086 E2A5 2A E5 E5 LD HL,(BLKNMBR) ;get sector number. +3087 E2A8 0E 00 LD C,0 ;set normal write flag. +3088 E2AA 3A C4 E5 LD A,(BLKMASK) ;determine if we have written the entire +3089 E2AD 47 LD B,A ;physical block. +3090 E2AE A5 AND L +3091 E2AF B8 CP B +3092 E2B0 23 INC HL ;prepare for the next one. +3093 E2B1 C2 9A E2 JP NZ,WTSEQ8 ;continue until (BLKMASK+1) sectors written. +3094 E2B4 E1 POP HL ;reset next sector number. +3095 E2B5 22 E5 E5 LD (BLKNMBR),HL +3096 E2B8 CD DA DD CALL DEFDMA ;and reset dma address. +3097 E2BB ; +3098 E2BB ; Normal disk write. Set the desired track and sector then +3099 E2BB ; do the actual write. +3100 E2BB ; +3101 E2BB CD D1 DB WTSEQ9: CALL TRKSEC1 ;determine track and sector for this write. +3102 E2BE C1 POP BC ;get write status flag. +3103 E2BF C5 PUSH BC +3104 E2C0 CD B8 DB CALL DOWRITE ;and write this out. +3105 E2C3 C1 POP BC +3106 E2C4 3A E3 E5 LD A,(SAVNREC) ;get number of records in file. +3107 E2C7 21 E1 E5 LD HL,SAVNXT ;get last record written. +3108 E2CA BE CP (HL) +3109 E2CB DA D2 E2 JP C,WTSEQ10 +3110 E2CE 77 LD (HL),A ;we have to update record count. +3111 E2CF 34 INC (HL) +3112 E2D0 0E 02 LD C,2 +3113 E2D2 ; +3114 E2D2 ;* This area has been patched to correct disk update problem +3115 E2D2 ;* when using blocking and de-blocking in the BIOS. +3116 E2D2 ; +3117 E2D2 00 WTSEQ10:NOP ;was 'dcr c' +3118 E2D3 00 NOP ;was 'dcr c' +3119 E2D4 21 00 00 LD HL,0 ;was 'jnz wtseq99' +3120 E2D7 ; +3121 E2D7 ; * End of patch. +3122 E2D7 ; +3123 E2D7 F5 PUSH AF +3124 E2D8 CD 69 DD CALL GETS2 ;set 'extent written to' flag. +3125 E2DB E6 7F AND 7FH ;(* clear bit 7 *) +3126 E2DD 77 LD (HL),A +3127 E2DE F1 POP AF ;get record count for this extent. +3128 E2DF FE 7F WTSEQ99:CP 127 ;is it full? +3129 E2E1 C2 00 E3 JP NZ,WTSEQ12 +3130 E2E4 3A D5 E5 LD A,(MODE) ;yes, are we in sequential mode? +3131 E2E7 FE 01 CP 1 +3132 E2E9 C2 00 E3 JP NZ,WTSEQ12 +3133 E2EC CD D2 DC CALL SETNREC ;yes, set next record number. +3134 E2EF CD 5A E1 CALL GETNEXT ;and get next empty space in directory. +3135 E2F2 21 45 DB LD HL,STATUS ;ok? +3136 E2F5 7E LD A,(HL) +3137 E2F6 B7 OR A +3138 E2F7 C2 FE E2 JP NZ,WTSEQ11 +3139 E2FA 3D DEC A ;yes, set record count to -1. +3140 E2FB 32 E3 E5 LD (SAVNREC),A +3141 E2FE 36 00 WTSEQ11:LD (HL),0 ;clear status. +3142 E300 C3 D2 DC WTSEQ12:JP SETNREC ;set next record to access. +3143 E303 ; +3144 E303 ; For random i/o, set the fcb for the desired record number +3145 E303 ; based on the 'r0,r1,r2' bytes. These bytes in the fcb are +3146 E303 ; used as follows: +3147 E303 ; +3148 E303 ; fcb+35 fcb+34 fcb+33 +3149 E303 ; | 'r-2' | 'r-1' | 'r-0' | +3150 E303 ; |7 0 | 7 0 | 7 0| +3151 E303 ; |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0| +3152 E303 ; | overflow | | extra | extent | record # | +3153 E303 ; | ______________| |_extent|__number___|_____________| +3154 E303 ; also 's2' +3155 E303 ; +3156 E303 ; On entry, register (C) contains 0ffh if this is a read +3157 E303 ; and thus we can not access unwritten disk space. Otherwise, +3158 E303 ; another extent will be opened (for writing) if required. +3159 E303 ; +3160 E303 AF POSITION: XOR A ;set random i/o flag. +3161 E304 32 D5 E5 LD (MODE),A +3162 E307 ; +3163 E307 ; Special entry (function #40). M/PM ? +3164 E307 ; +3165 E307 C5 POSITN1:PUSH BC ;save read/write flag. +3166 E308 2A 43 DB LD HL,(PARAMS) ;get address of fcb. +3167 E30B EB EX DE,HL +3168 E30C 21 21 00 LD HL,33 ;now get byte 'r0'. +3169 E30F 19 ADD HL,DE +3170 E310 7E LD A,(HL) +3171 E311 E6 7F AND 7FH ;keep bits 0-6 for the record number to access. +3172 E313 F5 PUSH AF +3173 E314 7E LD A,(HL) ;now get bit 7 of 'r0' and bits 0-3 of 'r1'. +3174 E315 17 RLA +3175 E316 23 INC HL +3176 E317 7E LD A,(HL) +3177 E318 17 RLA +3178 E319 E6 1F AND 1FH ;and save this in bits 0-4 of (C). +3179 E31B 4F LD C,A ;this is the extent byte. +3180 E31C 7E LD A,(HL) ;now get the extra extent byte. +3181 E31D 1F RRA +3182 E31E 1F RRA +3183 E31F 1F RRA +3184 E320 1F RRA +3185 E321 E6 0F AND 0FH +3186 E323 47 LD B,A ;and save it in (B). +3187 E324 F1 POP AF ;get record number back to (A). +3188 E325 23 INC HL ;check overflow byte 'r2'. +3189 E326 6E LD L,(HL) +3190 E327 2C INC L +3191 E328 2D DEC L +3192 E329 2E 06 LD L,6 ;prepare for error. +3193 E32B C2 8B E3 JP NZ,POSITN5 ;out of disk space error. +3194 E32E 21 20 00 LD HL,32 ;store record number into fcb. +3195 E331 19 ADD HL,DE +3196 E332 77 LD (HL),A +3197 E333 21 0C 00 LD HL,12 ;and now check the extent byte. +3198 E336 19 ADD HL,DE +3199 E337 79 LD A,C +3200 E338 96 SUB (HL) ;same extent as before? +3201 E339 C2 47 E3 JP NZ,POSITN2 +3202 E33C 21 0E 00 LD HL,14 ;yes, check extra extent byte 's2' also. +3203 E33F 19 ADD HL,DE +3204 E340 78 LD A,B +3205 E341 96 SUB (HL) +3206 E342 E6 7F AND 7FH +3207 E344 CA 7F E3 JP Z,POSITN3 ;same, we are almost done then. +3208 E347 ; +3209 E347 ; Get here when another extent is required. +3210 E347 ; +3211 E347 C5 POSITN2:PUSH BC +3212 E348 D5 PUSH DE +3213 E349 CD A2 E0 CALL CLOSEIT ;close current extent. +3214 E34C D1 POP DE +3215 E34D C1 POP BC +3216 E34E 2E 03 LD L,3 ;prepare for error. +3217 E350 3A 45 DB LD A,(STATUS) +3218 E353 3C INC A +3219 E354 CA 84 E3 JP Z,POSITN4 ;close error. +3220 E357 21 0C 00 LD HL,12 ;put desired extent into fcb now. +3221 E35A 19 ADD HL,DE +3222 E35B 71 LD (HL),C +3223 E35C 21 0E 00 LD HL,14 ;and store extra extent byte 's2'. +3224 E35F 19 ADD HL,DE +3225 E360 70 LD (HL),B +3226 E361 CD 51 E0 CALL OPENIT ;try and get this extent. +3227 E364 3A 45 DB LD A,(STATUS) ;was it there? +3228 E367 3C INC A +3229 E368 C2 7F E3 JP NZ,POSITN3 +3230 E36B C1 POP BC ;no. can we create a new one (writing?). +3231 E36C C5 PUSH BC +3232 E36D 2E 04 LD L,4 ;prepare for error. +3233 E36F 0C INC C +3234 E370 CA 84 E3 JP Z,POSITN4 ;nope, reading unwritten space error. +3235 E373 CD 24 E1 CALL GETEMPTY ;yes we can, try to find space. +3236 E376 2E 05 LD L,5 ;prepare for error. +3237 E378 3A 45 DB LD A,(STATUS) +3238 E37B 3C INC A +3239 E37C CA 84 E3 JP Z,POSITN4 ;out of space? +3240 E37F ; +3241 E37F ; Normal return location. Clear error code and return. +3242 E37F ; +3243 E37F C1 POSITN3:POP BC ;restore stack. +3244 E380 AF XOR A ;and clear error code byte. +3245 E381 C3 01 DB JP SETSTAT +3246 E384 ; +3247 E384 ; Error. Set the 's2' byte to indicate this (why?). +3248 E384 ; +3249 E384 E5 POSITN4:PUSH HL +3250 E385 CD 69 DD CALL GETS2 +3251 E388 36 C0 LD (HL),0C0H +3252 E38A E1 POP HL +3253 E38B ; +3254 E38B ; Return with error code (presently in L). +3255 E38B ; +3256 E38B C1 POSITN5:POP BC +3257 E38C 7D LD A,L ;get error code. +3258 E38D 32 45 DB LD (STATUS),A +3259 E390 C3 78 DD JP SETS2B7 +3260 E393 ; +3261 E393 ; Read a random record. +3262 E393 ; +3263 E393 0E FF READRAN:LD C,0FFH ;set 'read' status. +3264 E395 CD 03 E3 CALL POSITION ;position the file to proper record. +3265 E398 CC C1 E1 CALL Z,RDSEQ1 ;and read it as usual (if no errors). +3266 E39B C9 RET +3267 E39C ; +3268 E39C ; Write to a random record. +3269 E39C ; +3270 E39C 0E 00 WRITERAN: LD C,0 ;set 'writing' flag. +3271 E39E CD 03 E3 CALL POSITION ;position the file to proper record. +3272 E3A1 CC 03 E2 CALL Z,WTSEQ1 ;and write as usual (if no errors). +3273 E3A4 C9 RET +3274 E3A5 ; +3275 E3A5 ; Compute the random record number. Enter with (HL) pointing +3276 E3A5 ; to a fcb an (DE) contains a relative location of a record +3277 E3A5 ; number. On exit, (C) contains the 'r0' byte, (B) the 'r1' +3278 E3A5 ; byte, and (A) the 'r2' byte. +3279 E3A5 ; +3280 E3A5 ; On return, the zero flag is set if the record is within +3281 E3A5 ; bounds. Otherwise, an overflow occured. +3282 E3A5 ; +3283 E3A5 EB COMPRAND: EX DE,HL ;save fcb pointer in (DE). +3284 E3A6 19 ADD HL,DE ;compute relative position of record #. +3285 E3A7 4E LD C,(HL) ;get record number into (BC). +3286 E3A8 06 00 LD B,0 +3287 E3AA 21 0C 00 LD HL,12 ;now get extent. +3288 E3AD 19 ADD HL,DE +3289 E3AE 7E LD A,(HL) ;compute (BC)=(record #)+(extent)*128. +3290 E3AF 0F RRCA ;move lower bit into bit 7. +3291 E3B0 E6 80 AND 80H ;and ignore all other bits. +3292 E3B2 81 ADD A,C ;add to our record number. +3293 E3B3 4F LD C,A +3294 E3B4 3E 00 LD A,0 ;take care of any carry. +3295 E3B6 88 ADC A,B +3296 E3B7 47 LD B,A +3297 E3B8 7E LD A,(HL) ;now get the upper bits of extent into +3298 E3B9 0F RRCA ;bit positions 0-3. +3299 E3BA E6 0F AND 0FH ;and ignore all others. +3300 E3BC 80 ADD A,B ;add this in to 'r1' byte. +3301 E3BD 47 LD B,A +3302 E3BE 21 0E 00 LD HL,14 ;get the 's2' byte (extra extent). +3303 E3C1 19 ADD HL,DE +3304 E3C2 7E LD A,(HL) +3305 E3C3 87 ADD A,A ;and shift it left 4 bits (bits 4-7). +3306 E3C4 87 ADD A,A +3307 E3C5 87 ADD A,A +3308 E3C6 87 ADD A,A +3309 E3C7 F5 PUSH AF ;save carry flag (bit 0 of flag byte). +3310 E3C8 80 ADD A,B ;now add extra extent into 'r1'. +3311 E3C9 47 LD B,A +3312 E3CA F5 PUSH AF ;and save carry (overflow byte 'r2'). +3313 E3CB E1 POP HL ;bit 0 of (L) is the overflow indicator. +3314 E3CC 7D LD A,L +3315 E3CD E1 POP HL ;and same for first carry flag. +3316 E3CE B5 OR L ;either one of these set? +3317 E3CF E6 01 AND 01H ;only check the carry flags. +3318 E3D1 C9 RET +3319 E3D2 ; +3320 E3D2 ; Routine to setup the fcb (bytes 'r0', 'r1', 'r2') to +3321 E3D2 ; reflect the last record used for a random (or other) file. +3322 E3D2 ; This reads the directory and looks at all extents computing +3323 E3D2 ; the largerst record number for each and keeping the maximum +3324 E3D2 ; value only. Then 'r0', 'r1', and 'r2' will reflect this +3325 E3D2 ; maximum record number. This is used to compute the space used +3326 E3D2 ; by a random file. +3327 E3D2 ; +3328 E3D2 0E 0C RANSIZE:LD C,12 ;look thru directory for first entry with +3329 E3D4 CD 18 DF CALL FINDFST ;this name. +3330 E3D7 2A 43 DB LD HL,(PARAMS) ;zero out the 'r0, r1, r2' bytes. +3331 E3DA 11 21 00 LD DE,33 +3332 E3DD 19 ADD HL,DE +3333 E3DE E5 PUSH HL +3334 E3DF 72 LD (HL),D ;note that (D)=0. +3335 E3E0 23 INC HL +3336 E3E1 72 LD (HL),D +3337 E3E2 23 INC HL +3338 E3E3 72 LD (HL),D +3339 E3E4 CD F5 DD RANSIZ1:CALL CKFILPOS ;is there an extent to process? +3340 E3E7 CA 0C E4 JP Z,RANSIZ3 ;no, we are done. +3341 E3EA CD 5E DD CALL FCB2HL ;set (HL) pointing to proper fcb in dir. +3342 E3ED 11 0F 00 LD DE,15 ;point to last record in extent. +3343 E3F0 CD A5 E3 CALL COMPRAND ;and compute random parameters. +3344 E3F3 E1 POP HL +3345 E3F4 E5 PUSH HL ;now check these values against those +3346 E3F5 5F LD E,A ;already in fcb. +3347 E3F6 79 LD A,C ;the carry flag will be set if those +3348 E3F7 96 SUB (HL) ;in the fcb represent a larger size than +3349 E3F8 23 INC HL ;this extent does. +3350 E3F9 78 LD A,B +3351 E3FA 9E SBC A,(HL) +3352 E3FB 23 INC HL +3353 E3FC 7B LD A,E +3354 E3FD 9E SBC A,(HL) +3355 E3FE DA 06 E4 JP C,RANSIZ2 +3356 E401 73 LD (HL),E ;we found a larger (in size) extent. +3357 E402 2B DEC HL ;stuff these values into fcb. +3358 E403 70 LD (HL),B +3359 E404 2B DEC HL +3360 E405 71 LD (HL),C +3361 E406 CD 2D DF RANSIZ2:CALL FINDNXT ;now get the next extent. +3362 E409 C3 E4 E3 JP RANSIZ1 ;continue til all done. +3363 E40C E1 RANSIZ3:POP HL ;we are done, restore the stack and +3364 E40D C9 RET ;return. +3365 E40E ; +3366 E40E ; Function to return the random record position of a given +3367 E40E ; file which has been read in sequential mode up to now. +3368 E40E ; +3369 E40E 2A 43 DB SETRAN: LD HL,(PARAMS) ;point to fcb. +3370 E411 11 20 00 LD DE,32 ;and to last used record. +3371 E414 CD A5 E3 CALL COMPRAND ;compute random position. +3372 E417 21 21 00 LD HL,33 ;now stuff these values into fcb. +3373 E41A 19 ADD HL,DE +3374 E41B 71 LD (HL),C ;move 'r0'. +3375 E41C 23 INC HL +3376 E41D 70 LD (HL),B ;and 'r1'. +3377 E41E 23 INC HL +3378 E41F 77 LD (HL),A ;and lastly 'r2'. +3379 E420 C9 RET +3380 E421 ; +3381 E421 ; This routine select the drive specified in (ACTIVE) and +3382 E421 ; update the login vector and bitmap table if this drive was +3383 E421 ; not already active. +3384 E421 ; +3385 E421 2A AF E5 LOGINDRV: LD HL,(LOGIN) ;get the login vector. +3386 E424 3A 42 DB LD A,(ACTIVE) ;get the default drive. +3387 E427 4F LD C,A +3388 E428 CD EA DC CALL SHIFTR ;position active bit for this drive +3389 E42B E5 PUSH HL ;into bit 0. +3390 E42C EB EX DE,HL +3391 E42D CD 59 DB CALL SELECT ;select this drive. +3392 E430 E1 POP HL +3393 E431 CC 47 DB CALL Z,SLCTERR ;valid drive? +3394 E434 7D LD A,L ;is this a newly activated drive? +3395 E435 1F RRA +3396 E436 D8 RET C +3397 E437 2A AF E5 LD HL,(LOGIN) ;yes, update the login vector. +3398 E43A 4D LD C,L +3399 E43B 44 LD B,H +3400 E43C CD 0B DD CALL SETBIT +3401 E43F 22 AF E5 LD (LOGIN),HL ;and save. +3402 E442 C3 A3 DE JP BITMAP ;now update the bitmap. +3403 E445 ; +3404 E445 ; Function to set the active disk number. +3405 E445 ; +3406 E445 3A D6 E5 SETDSK: LD A,(EPARAM) ;get parameter passed and see if this +3407 E448 21 42 DB LD HL,ACTIVE ;represents a change in drives. +3408 E44B BE CP (HL) +3409 E44C C8 RET Z +3410 E44D 77 LD (HL),A ;yes it does, log it in. +3411 E44E C3 21 E4 JP LOGINDRV +3412 E451 ; +3413 E451 ; This is the 'auto disk select' routine. The firsst byte +3414 E451 ; of the fcb is examined for a drive specification. If non +3415 E451 ; zero then the drive will be selected and loged in. +3416 E451 ; +3417 E451 3E FF AUTOSEL:LD A,0FFH ;say 'auto-select activated'. +3418 E453 32 DE E5 LD (AUTO),A +3419 E456 2A 43 DB LD HL,(PARAMS) ;get drive specified. +3420 E459 7E LD A,(HL) +3421 E45A E6 1F AND 1FH ;look at lower 5 bits. +3422 E45C 3D DEC A ;adjust for (1=A, 2=B) etc. +3423 E45D 32 D6 E5 LD (EPARAM),A ;and save for the select routine. +3424 E460 FE 1E CP 1EH ;check for 'no change' condition. +3425 E462 D2 75 E4 JP NC,AUTOSL1 ;yes, don't change. +3426 E465 3A 42 DB LD A,(ACTIVE) ;we must change, save currently active +3427 E468 32 DF E5 LD (OLDDRV),A ;drive. +3428 E46B 7E LD A,(HL) ;and save first byte of fcb also. +3429 E46C 32 E0 E5 LD (AUTOFLAG),A ;this must be non-zero. +3430 E46F E6 E0 AND 0E0H ;whats this for (bits 6,7 are used for +3431 E471 77 LD (HL),A ;something)? +3432 E472 CD 45 E4 CALL SETDSK ;select and log in this drive. +3433 E475 3A 41 DB AUTOSL1:LD A,(USERNO) ;move user number into fcb. +3434 E478 2A 43 DB LD HL,(PARAMS) ;(* upper half of first byte *) +3435 E47B B6 OR (HL) +3436 E47C 77 LD (HL),A +3437 E47D C9 RET ;and return (all done). +3438 E47E ; +3439 E47E ; Function to return the current cp/m version number. +3440 E47E ; +3441 E47E 3E 22 GETVER: LD A,022H ;version 2.2 +3442 E480 C3 01 DB JP SETSTAT +3443 E483 ; +3444 E483 ; Function to reset the disk system. +3445 E483 ; +3446 E483 21 00 00 RSTDSK: LD HL,0 ;clear write protect status and log +3447 E486 22 AD E5 LD (WRTPRT),HL ;in vector. +3448 E489 22 AF E5 LD (LOGIN),HL +3449 E48C AF XOR A ;select drive 'A'. +3450 E48D 32 42 DB LD (ACTIVE),A +3451 E490 21 80 00 LD HL,TBUFF ;setup default dma address. +3452 E493 22 B1 E5 LD (USERDMA),HL +3453 E496 CD DA DD CALL DEFDMA +3454 E499 C3 21 E4 JP LOGINDRV ;now log in drive 'A'. +3455 E49C ; +3456 E49C ; Function to open a specified file. +3457 E49C ; +3458 E49C CD 72 DD OPENFIL:CALL CLEARS2 ;clear 's2' byte. +3459 E49F CD 51 E4 CALL AUTOSEL ;select proper disk. +3460 E4A2 C3 51 E0 JP OPENIT ;and open the file. +3461 E4A5 ; +3462 E4A5 ; Function to close a specified file. +3463 E4A5 ; +3464 E4A5 CD 51 E4 CLOSEFIL: CALL AUTOSEL ;select proper disk. +3465 E4A8 C3 A2 E0 JP CLOSEIT ;and close the file. +3466 E4AB ; +3467 E4AB ; Function to return the first occurence of a specified file +3468 E4AB ; name. If the first byte of the fcb is '?' then the name will +3469 E4AB ; not be checked (get the first entry no matter what). +3470 E4AB ; +3471 E4AB 0E 00 GETFST: LD C,0 ;prepare for special search. +3472 E4AD EB EX DE,HL +3473 E4AE 7E LD A,(HL) ;is first byte a '?'? +3474 E4AF FE 3F CP '?' +3475 E4B1 CA C2 E4 JP Z,GETFST1 ;yes, just get very first entry (zero length match). +3476 E4B4 CD A6 DC CALL SETEXT ;get the extension byte from fcb. +3477 E4B7 7E LD A,(HL) ;is it '?'? if yes, then we want +3478 E4B8 FE 3F CP '?' ;an entry with a specific 's2' byte. +3479 E4BA C4 72 DD CALL NZ,CLEARS2 ;otherwise, look for a zero 's2' byte. +3480 E4BD CD 51 E4 CALL AUTOSEL ;select proper drive. +3481 E4C0 0E 0F LD C,15 ;compare bytes 0-14 in fcb (12&13 excluded). +3482 E4C2 CD 18 DF GETFST1:CALL FINDFST ;find an entry and then move it into +3483 E4C5 C3 E9 DD JP MOVEDIR ;the users dma space. +3484 E4C8 ; +3485 E4C8 ; Function to return the next occurence of a file name. +3486 E4C8 ; +3487 E4C8 2A D9 E5 GETNXT: LD HL,(SAVEFCB) ;restore pointers. note that no +3488 E4CB 22 43 DB LD (PARAMS),HL ;other dbos calls are allowed. +3489 E4CE CD 51 E4 CALL AUTOSEL ;no error will be returned, but the +3490 E4D1 CD 2D DF CALL FINDNXT ;results will be wrong. +3491 E4D4 C3 E9 DD JP MOVEDIR +3492 E4D7 ; +3493 E4D7 ; Function to delete a file by name. +3494 E4D7 ; +3495 E4D7 CD 51 E4 DELFILE:CALL AUTOSEL ;select proper drive. +3496 E4DA CD 9C DF CALL ERAFILE ;erase the file. +3497 E4DD C3 01 DF JP STSTATUS ;set status and return. +3498 E4E0 ; +3499 E4E0 ; Function to execute a sequential read of the specified +3500 E4E0 ; record number. +3501 E4E0 ; +3502 E4E0 CD 51 E4 READSEQ:CALL AUTOSEL ;select proper drive then read. +3503 E4E3 C3 BC E1 JP RDSEQ +3504 E4E6 ; +3505 E4E6 ; Function to write the net sequential record. +3506 E4E6 ; +3507 E4E6 CD 51 E4 WRTSEQ: CALL AUTOSEL ;select proper drive then write. +3508 E4E9 C3 FE E1 JP WTSEQ +3509 E4EC ; +3510 E4EC ; Create a file function. +3511 E4EC ; +3512 E4EC CD 72 DD FCREATE:CALL CLEARS2 ;clear the 's2' byte on all creates. +3513 E4EF CD 51 E4 CALL AUTOSEL ;select proper drive and get the next +3514 E4F2 C3 24 E1 JP GETEMPTY ;empty directory space. +3515 E4F5 ; +3516 E4F5 ; Function to rename a file. +3517 E4F5 ; +3518 E4F5 CD 51 E4 RENFILE:CALL AUTOSEL ;select proper drive and then switch +3519 E4F8 CD 16 E0 CALL CHGNAMES ;file names. +3520 E4FB C3 01 DF JP STSTATUS +3521 E4FE ; +3522 E4FE ; Function to return the login vector. +3523 E4FE ; +3524 E4FE 2A AF E5 GETLOG: LD HL,(LOGIN) +3525 E501 C3 29 E5 JP GETPRM1 +3526 E504 ; +3527 E504 ; Function to return the current disk assignment. +3528 E504 ; +3529 E504 3A 42 DB GETCRNT:LD A,(ACTIVE) +3530 E507 C3 01 DB JP SETSTAT +3531 E50A ; +3532 E50A ; Function to set the dma address. +3533 E50A ; +3534 E50A EB PUTDMA: EX DE,HL +3535 E50B 22 B1 E5 LD (USERDMA),HL ;save in our space and then get to +3536 E50E C3 DA DD JP DEFDMA ;the bios with this also. +3537 E511 ; +3538 E511 ; Function to return the allocation vector. +3539 E511 ; +3540 E511 2A BF E5 GETALOC:LD HL,(ALOCVECT) +3541 E514 C3 29 E5 JP GETPRM1 +3542 E517 ; +3543 E517 ; Function to return the read-only status vector. +3544 E517 ; +3545 E517 2A AD E5 GETROV: LD HL,(WRTPRT) +3546 E51A C3 29 E5 JP GETPRM1 +3547 E51D ; +3548 E51D ; Function to set the file attributes (read-only, system). +3549 E51D ; +3550 E51D CD 51 E4 SETATTR:CALL AUTOSEL ;select proper drive then save attributes. +3551 E520 CD 3B E0 CALL SAVEATTR +3552 E523 C3 01 DF JP STSTATUS +3553 E526 ; +3554 E526 ; Function to return the address of the disk parameter block +3555 E526 ; for the current drive. +3556 E526 ; +3557 E526 2A BB E5 GETPARM:LD HL,(DISKPB) +3558 E529 22 45 DB GETPRM1:LD (STATUS),HL +3559 E52C C9 RET +3560 E52D ; +3561 E52D ; Function to get or set the user number. If (E) was (FF) +3562 E52D ; then this is a request to return the current user number. +3563 E52D ; Else set the user number from (E). +3564 E52D ; +3565 E52D 3A D6 E5 GETUSER:LD A,(EPARAM) ;get parameter. +3566 E530 FE FF CP 0FFH ;get user number? +3567 E532 C2 3B E5 JP NZ,SETUSER +3568 E535 3A 41 DB LD A,(USERNO) ;yes, just do it. +3569 E538 C3 01 DB JP SETSTAT +3570 E53B E6 1F SETUSER:AND 1FH ;no, we should set it instead. keep low +3571 E53D 32 41 DB LD (USERNO),A ;bits (0-4) only. +3572 E540 C9 RET +3573 E541 ; +3574 E541 ; Function to read a random record from a file. +3575 E541 ; +3576 E541 CD 51 E4 RDRANDOM: CALL AUTOSEL ;select proper drive and read. +3577 E544 C3 93 E3 JP READRAN +3578 E547 ; +3579 E547 ; Function to compute the file size for random files. +3580 E547 ; +3581 E547 CD 51 E4 WTRANDOM: CALL AUTOSEL ;select proper drive and write. +3582 E54A C3 9C E3 JP WRITERAN +3583 E54D ; +3584 E54D ; Function to compute the size of a random file. +3585 E54D ; +3586 E54D CD 51 E4 FILESIZE: CALL AUTOSEL ;select proper drive and check file length +3587 E550 C3 D2 E3 JP RANSIZE +3588 E553 ; +3589 E553 ; Function #37. This allows a program to log off any drives. +3590 E553 ; On entry, set (DE) to contain a word with bits set for those +3591 E553 ; drives that are to be logged off. The log-in vector and the +3592 E553 ; write protect vector will be updated. This must be a M/PM +3593 E553 ; special function. +3594 E553 ; +3595 E553 2A 43 DB LOGOFF: LD HL,(PARAMS) ;get drives to log off. +3596 E556 7D LD A,L ;for each bit that is set, we want +3597 E557 2F CPL ;to clear that bit in (LOGIN) +3598 E558 5F LD E,A ;and (WRTPRT). +3599 E559 7C LD A,H +3600 E55A 2F CPL +3601 E55B 2A AF E5 LD HL,(LOGIN) ;reset the login vector. +3602 E55E A4 AND H +3603 E55F 57 LD D,A +3604 E560 7D LD A,L +3605 E561 A3 AND E +3606 E562 5F LD E,A +3607 E563 2A AD E5 LD HL,(WRTPRT) +3608 E566 EB EX DE,HL +3609 E567 22 AF E5 LD (LOGIN),HL ;and save. +3610 E56A 7D LD A,L ;now do the write protect vector. +3611 E56B A3 AND E +3612 E56C 6F LD L,A +3613 E56D 7C LD A,H +3614 E56E A2 AND D +3615 E56F 67 LD H,A +3616 E570 22 AD E5 LD (WRTPRT),HL ;and save. all done. +3617 E573 C9 RET +3618 E574 ; +3619 E574 ; Get here to return to the user. +3620 E574 ; +3621 E574 3A DE E5 GOBACK: LD A,(AUTO) ;was auto select activated? +3622 E577 B7 OR A +3623 E578 CA 91 E5 JP Z,GOBACK1 +3624 E57B 2A 43 DB LD HL,(PARAMS) ;yes, but was a change made? +3625 E57E 36 00 LD (HL),0 ;(* reset first byte of fcb *) +3626 E580 3A E0 E5 LD A,(AUTOFLAG) +3627 E583 B7 OR A +3628 E584 CA 91 E5 JP Z,GOBACK1 +3629 E587 77 LD (HL),A ;yes, reset first byte properly. +3630 E588 3A DF E5 LD A,(OLDDRV) ;and get the old drive and select it. +3631 E58B 32 D6 E5 LD (EPARAM),A +3632 E58E CD 45 E4 CALL SETDSK +3633 E591 2A 0F DB GOBACK1:LD HL,(USRSTACK) ;reset the users stack pointer. +3634 E594 F9 LD SP,HL +3635 E595 2A 45 DB LD HL,(STATUS) ;get return status. +3636 E598 7D LD A,L ;force version 1.4 compatability. +3637 E599 44 LD B,H +3638 E59A C9 RET ;and go back to user. +3639 E59B ; +3640 E59B ; Function #40. This is a special entry to do random i/o. +3641 E59B ; For the case where we are writing to unused disk space, this +3642 E59B ; space will be zeroed out first. This must be a M/PM special +3643 E59B ; purpose function, because why would any normal program even +3644 E59B ; care about the previous contents of a sector about to be +3645 E59B ; written over. +3646 E59B ; +3647 E59B CD 51 E4 WTSPECL:CALL AUTOSEL ;select proper drive. +3648 E59E 3E 02 LD A,2 ;use special write mode. +3649 E5A0 32 D5 E5 LD (MODE),A +3650 E5A3 0E 00 LD C,0 ;set write indicator. +3651 E5A5 CD 07 E3 CALL POSITN1 ;position the file. +3652 E5A8 CC 03 E2 CALL Z,WTSEQ1 ;and write (if no errors). +3653 E5AB C9 RET +3654 E5AC ; +3655 E5AC ;************************************************************** +3656 E5AC ;* +3657 E5AC ;* BDOS data storage pool. +3658 E5AC ;* +3659 E5AC ;************************************************************** +3660 E5AC ; +3661 E5AC E5 EMPTYFCB: .DB 0E5H ;empty directory segment indicator. +3662 E5AD 00 00 WRTPRT: .DW 0 ;write protect status for all 16 drives. +3663 E5AF 00 00 LOGIN: .DW 0 ;drive active word (1 bit per drive). +3664 E5B1 80 00 USERDMA:.DW 080H ;user's dma address (defaults to 80h). +3665 E5B3 ; +3666 E5B3 ; Scratch areas from parameter block. +3667 E5B3 ; +3668 E5B3 00 00 SCRATCH1: .DW 0 ;relative position within dir segment for file (0-3). +3669 E5B5 00 00 SCRATCH2: .DW 0 ;last selected track number. +3670 E5B7 00 00 SCRATCH3: .DW 0 ;last selected sector number. +3671 E5B9 ; +3672 E5B9 ; Disk storage areas from parameter block. +3673 E5B9 ; +3674 E5B9 00 00 DIRBUF: .DW 0 ;address of directory buffer to use. +3675 E5BB 00 00 DISKPB: .DW 0 ;contains address of disk parameter block. +3676 E5BD 00 00 CHKVECT:.DW 0 ;address of check vector. +3677 E5BF 00 00 ALOCVECT: .DW 0 ;address of allocation vector (bit map). +3678 E5C1 ; +3679 E5C1 ; Parameter block returned from the bios. +3680 E5C1 ; +3681 E5C1 00 00 SECTORS:.DW 0 ;sectors per track from bios. +3682 E5C3 00 BLKSHFT:.DB 0 ;block shift. +3683 E5C4 00 BLKMASK:.DB 0 ;block mask. +3684 E5C5 00 EXTMASK:.DB 0 ;extent mask. +3685 E5C6 00 00 DSKSIZE:.DW 0 ;disk size from bios (number of blocks-1). +3686 E5C8 00 00 DIRSIZE:.DW 0 ;directory size. +3687 E5CA 00 00 ALLOC0: .DW 0 ;storage for first bytes of bit map (dir space used). +3688 E5CC 00 00 ALLOC1: .DW 0 +3689 E5CE 00 00 OFFSET: .DW 0 ;first usable track number. +3690 E5D0 00 00 XLATE: .DW 0 ;sector translation table address. +3691 E5D2 ; +3692 E5D2 ; +3693 E5D2 00 CLOSEFLG: .DB 0 ;close flag (=0ffh is extent written ok). +3694 E5D3 00 RDWRTFLG: .DB 0 ;read/write flag (0ffh=read, 0=write). +3695 E5D4 00 FNDSTAT:.DB 0 ;filename found status (0=found first entry). +3696 E5D5 00 MODE: .DB 0 ;I/o mode select (0=random, 1=sequential, 2=special random). +3697 E5D6 00 EPARAM: .DB 0 ;storage for register (E) on entry to bdos. +3698 E5D7 00 RELBLOCK: .DB 0 ;relative position within fcb of block number written. +3699 E5D8 00 COUNTER:.DB 0 ;byte counter for directory name searches. +3700 E5D9 00 00 00 00 SAVEFCB:.DW 0,0 ;save space for address of fcb (for directory searches). +3701 E5DD 00 BIGDISK:.DB 0 ;if =0 then disk is > 256 blocks long. +3702 E5DE 00 AUTO: .DB 0 ;if non-zero, then auto select activated. +3703 E5DF 00 OLDDRV: .DB 0 ;on auto select, storage for previous drive. +3704 E5E0 00 AUTOFLAG: .DB 0 ;if non-zero, then auto select changed drives. +3705 E5E1 00 SAVNXT: .DB 0 ;storage for next record number to access. +3706 E5E2 00 SAVEXT: .DB 0 ;storage for extent number of file. +3707 E5E3 00 00 SAVNREC:.DW 0 ;storage for number of records in file. +3708 E5E5 00 00 BLKNMBR:.DW 0 ;block number (physical sector) used within a file or logical sect +3709 E5E7 00 00 LOGSECT:.DW 0 ;starting logical (128 byte) sector of block (physical sector). +3710 E5E9 00 FCBPOS: .DB 0 ;relative position within buffer for fcb of file of interest. +3711 E5EA 00 00 FILEPOS:.DW 0 ;files position within directory (0 to max entries -1). +3712 E5EC ; +3713 E5EC ; Disk directory buffer checksum bytes. One for each of the +3714 E5EC ; 16 possible drives. +3715 E5EC ; +3716 E5EC 000000000000CKSUMTBL: .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +3716 E5F2 00000000000000000000 +3717 E5FC ; +3718 E5FC ; Extra space ? +3719 E5FC ; +3720 E5FC 00 00 00 00 .DB 0,0,0,0 +3721 E600 ; +3722 E600 ;************************************************************** +3723 E600 ;* +3724 E600 ;* B I O S J U M P T A B L E +3725 E600 ;* +3726 E600 ;************************************************************** +3727 E600 ; +3728 E600 C3 00 00 BOOT: JP 0 ;NOTE WE USE FAKE DESTINATIONS +3729 E603 C3 00 00 WBOOT: JP 0 +3730 E606 C3 00 00 CONST: JP 0 +3731 E609 C3 00 00 CONIN: JP 0 +3732 E60C C3 00 00 CONOUT: JP 0 +3733 E60F C3 00 00 LIST: JP 0 +3734 E612 C3 00 00 PUNCH: JP 0 +3735 E615 C3 00 00 READER: JP 0 +3736 E618 C3 00 00 HOME: JP 0 +3737 E61B C3 00 00 SELDSK: JP 0 +3738 E61E C3 00 00 SETTRK: JP 0 +3739 E621 C3 00 00 SETSEC: JP 0 +3740 E624 C3 00 00 SETDMA: JP 0 +3741 E627 C3 00 00 READ: JP 0 +3742 E62A C3 00 00 WRITE: JP 0 +3743 E62D C3 00 00 PRSTAT: JP 0 +3744 E630 C3 00 00 SECTRN: JP 0 +3745 E633 ; +3746 E633 ;* +3747 E633 ;****************** E N D O F C P / M ***************** +3748 E633 ;* +3749 E633 +3750 E633 .END +tasm: Number of errors = 0 diff --git a/Z80 CPM and bootloader (basmon)/source/DOWNLOAD.LST b/Z80 CPM and bootloader (basmon)/source/DOWNLOAD.LST new file mode 100644 index 0000000..98fa498 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/DOWNLOAD.LST @@ -0,0 +1,290 @@ +0001 0000 ;================================================================================== +0002 0000 ; Contents of this file are copyright Grant Searle +0003 0000 ; HEX routine from Joel Owens. +0004 0000 ; +0005 0000 ; You have permission to use this for NON COMMERCIAL USE ONLY +0006 0000 ; If you wish to use it elsewhere, please include an acknowledgement to myself. +0007 0000 ; +0008 0000 ; http://searle.hostei.com/grant/index.html +0009 0000 ; +0010 0000 ; eMail: home.micros01@btinternet.com +0011 0000 ; +0012 0000 ; If the above don't work, please perform an Internet search to see if I have +0013 0000 ; updated the web page hosting service. +0014 0000 ; +0015 0000 ;================================================================================== +0016 0000 +0017 0000 TPA .EQU 100H +0018 0000 REBOOT .EQU 0H +0019 0000 BDOS .EQU 5H +0020 0000 CONIO .EQU 6 +0021 0000 CONINP .EQU 1 +0022 0000 CONOUT .EQU 2 +0023 0000 PSTRING .EQU 9 +0024 0000 MAKEF .EQU 22 +0025 0000 CLOSEF .EQU 16 +0026 0000 WRITES .EQU 21 +0027 0000 DELF .EQU 19 +0028 0000 SETUSR .EQU 32 +0029 0000 +0030 0000 CR .EQU 0DH +0031 0000 LF .EQU 0AH +0032 0000 +0033 0000 FCB .EQU 05CH +0034 0000 BUFF .EQU 080H +0035 0000 +0036 0100 .ORG TPA +0037 0100 +0038 0100 +0039 0100 3E 00 LD A,0 +0040 0102 32 6D 02 LD (buffPos),A +0041 0105 32 71 02 LD (checkSum),A +0042 0108 32 72 02 LD (byteCount),A +0043 010B 32 70 02 LD (printCount),A +0044 010E 21 80 00 LD HL,BUFF +0045 0111 22 6E 02 LD (buffPtr),HL +0046 0114 +0047 0114 +0048 0114 CD 39 02 WAITLT: CALL GETCHR +0049 0117 FE 55 CP 'U' +0050 0119 CA 2A 02 JP Z,SETUSER +0051 011C FE 3A CP ':' +0052 011E 20 F4 JR NZ,WAITLT +0053 0120 +0054 0120 +0055 0120 0E 13 LD C,DELF +0056 0122 11 5C 00 LD DE,FCB +0057 0125 CD 05 00 CALL BDOS +0058 0128 +0059 0128 0E 16 LD C,MAKEF +0060 012A 11 5C 00 LD DE,FCB +0061 012D CD 05 00 CALL BDOS +0062 0130 +0063 0130 GETHEX: +0064 0130 CD 39 02 CALL GETCHR +0065 0133 FE 3E CP '>' +0066 0135 28 61 JR Z,CLOSE +0067 0137 47 LD B,A +0068 0138 C5 PUSH BC +0069 0139 CD 39 02 CALL GETCHR +0070 013C C1 POP BC +0071 013D 4F LD C,A +0072 013E +0073 013E CD 4C 02 CALL BCTOA +0074 0141 +0075 0141 47 LD B,A +0076 0142 3A 71 02 LD A,(checkSum) +0077 0145 80 ADD A,B +0078 0146 32 71 02 LD (checkSum),A +0079 0149 3A 72 02 LD A,(byteCount) +0080 014C 3C INC A +0081 014D 32 72 02 LD (byteCount),A +0082 0150 +0083 0150 78 LD A,B +0084 0151 +0085 0151 2A 6E 02 LD HL,(buffPtr) +0086 0154 +0087 0154 77 LD (HL),A +0088 0155 23 INC HL +0089 0156 22 6E 02 LD (buffPtr),HL +0090 0159 +0091 0159 3A 6D 02 LD A,(buffPos) +0092 015C 3C INC A +0093 015D 32 6D 02 LD (buffPos),A +0094 0160 FE 80 CP 80H +0095 0162 +0096 0162 20 32 JR NZ,NOWRITE +0097 0164 +0098 0164 0E 15 LD C,WRITES +0099 0166 11 5C 00 LD DE,FCB +0100 0169 CD 05 00 CALL BDOS +0101 016C 3E 2E LD A,'.' +0102 016E CD 45 02 CALL PUTCHR +0103 0171 +0104 0171 ; New line every 8K (64 dots) +0105 0171 3A 70 02 LD A,(printCount) +0106 0174 3C INC A +0107 0175 FE 40 CP 64 +0108 0177 20 0F JR NZ,noCRLF +0109 0179 32 70 02 LD (printCount),A +0110 017C 3E 0D LD A,CR +0111 017E CD 45 02 CALL PUTCHR +0112 0181 3E 0A LD A,LF +0113 0183 CD 45 02 CALL PUTCHR +0114 0186 3E 00 LD A,0 +0115 0188 32 70 02 noCRLF: LD (printCount),A +0116 018B +0117 018B 21 80 00 LD HL,BUFF +0118 018E 22 6E 02 LD (buffPtr),HL +0119 0191 +0120 0191 3E 00 LD A,0 +0121 0193 32 6D 02 LD (buffPos),A +0122 0196 NOWRITE: +0123 0196 18 98 JR GETHEX +0124 0198 +0125 0198 +0126 0198 CLOSE: +0127 0198 +0128 0198 3A 6D 02 LD A,(buffPos) +0129 019B FE 00 CP 0 +0130 019D 28 0D JR Z,NOWRITE2 +0131 019F +0132 019F 0E 15 LD C,WRITES +0133 01A1 11 5C 00 LD DE,FCB +0134 01A4 CD 05 00 CALL BDOS +0135 01A7 3E 2E LD A,'.' +0136 01A9 CD 45 02 CALL PUTCHR +0137 01AC +0138 01AC NOWRITE2: +0139 01AC 0E 10 LD C,CLOSEF +0140 01AE 11 5C 00 LD DE,FCB +0141 01B1 CD 05 00 CALL BDOS +0142 01B4 +0143 01B4 ; Byte count (lower 8 bits) +0144 01B4 CD 39 02 CALL GETCHR +0145 01B7 47 LD B,A +0146 01B8 C5 PUSH BC +0147 01B9 CD 39 02 CALL GETCHR +0148 01BC C1 POP BC +0149 01BD 4F LD C,A +0150 01BE +0151 01BE CD 4C 02 CALL BCTOA +0152 01C1 47 LD B,A +0153 01C2 3A 72 02 LD A,(byteCount) +0154 01C5 90 SUB B +0155 01C6 FE 00 CP 0 +0156 01C8 28 1A JR Z,byteCountOK +0157 01CA +0158 01CA 3E 0D LD A,CR +0159 01CC CD 45 02 CALL PUTCHR +0160 01CF 3E 0A LD A,LF +0161 01D1 CD 45 02 CALL PUTCHR +0162 01D4 +0163 01D4 11 91 02 LD DE,countErrMess +0164 01D7 0E 09 LD C,PSTRING +0165 01D9 CD 05 00 CALL BDOS +0166 01DC +0167 01DC ; Sink remaining 2 bytes +0168 01DC CD 39 02 CALL GETCHR +0169 01DF CD 39 02 CALL GETCHR +0170 01E2 +0171 01E2 18 3C JR FINISH +0172 01E4 +0173 01E4 byteCountOK: +0174 01E4 +0175 01E4 ; Checksum +0176 01E4 CD 39 02 CALL GETCHR +0177 01E7 47 LD B,A +0178 01E8 C5 PUSH BC +0179 01E9 CD 39 02 CALL GETCHR +0180 01EC C1 POP BC +0181 01ED 4F LD C,A +0182 01EE +0183 01EE CD 4C 02 CALL BCTOA +0184 01F1 47 LD B,A +0185 01F2 3A 71 02 LD A,(checkSum) +0186 01F5 90 SUB B +0187 01F6 FE 00 CP 0 +0188 01F8 28 14 JR Z,checksumOK +0189 01FA +0190 01FA 3E 0D LD A,CR +0191 01FC CD 45 02 CALL PUTCHR +0192 01FF 3E 0A LD A,LF +0193 0201 CD 45 02 CALL PUTCHR +0194 0204 +0195 0204 11 76 02 LD DE,chkErrMess +0196 0207 0E 09 LD C,PSTRING +0197 0209 CD 05 00 CALL BDOS +0198 020C 18 12 JR FINISH +0199 020E +0200 020E checksumOK: +0201 020E 3E 0D LD A,CR +0202 0210 CD 45 02 CALL PUTCHR +0203 0213 3E 0A LD A,LF +0204 0215 CD 45 02 CALL PUTCHR +0205 0218 +0206 0218 11 73 02 LD DE,OKMess +0207 021B 0E 09 LD C,PSTRING +0208 021D CD 05 00 CALL BDOS +0209 0220 +0210 0220 +0211 0220 +0212 0220 FINISH: +0213 0220 0E 20 LD C,SETUSR +0214 0222 1E 00 LD E,0 +0215 0224 CD 05 00 CALL BDOS +0216 0227 +0217 0227 C3 00 00 JP REBOOT +0218 022A +0219 022A +0220 022A SETUSER: +0221 022A CD 39 02 CALL GETCHR +0222 022D CD 65 02 CALL HEX2VAL +0223 0230 5F LD E,A +0224 0231 0E 20 LD C,SETUSR +0225 0233 CD 05 00 CALL BDOS +0226 0236 C3 14 01 JP WAITLT +0227 0239 +0228 0239 +0229 0239 ; Get a char into A +0230 0239 ;GETCHR: LD C,CONINP +0231 0239 ; CALL BDOS +0232 0239 ; RET +0233 0239 +0234 0239 ; Wait for a char into A (no echo) +0235 0239 GETCHR: +0236 0239 1E FF LD E,$FF +0237 023B 0E 06 LD C,CONIO +0238 023D CD 05 00 CALL BDOS +0239 0240 FE 00 CP 0 +0240 0242 28 F5 JR Z,GETCHR +0241 0244 C9 RET +0242 0245 +0243 0245 ; Write A to output +0244 0245 0E 02 PUTCHR: LD C,CONOUT +0245 0247 5F LD E,A +0246 0248 CD 05 00 CALL BDOS +0247 024B C9 RET +0248 024C +0249 024C +0250 024C ;------------------------------------------------------------------------------ +0251 024C ; Convert ASCII characters in B C registers to a byte value in A +0252 024C ;------------------------------------------------------------------------------ +0253 024C 78 BCTOA LD A,B ; Move the hi order byte to A +0254 024D D6 30 SUB $30 ; Take it down from Ascii +0255 024F FE 0A CP $0A ; Are we in the 0-9 range here? +0256 0251 38 02 JR C,BCTOA1 ; If so, get the next nybble +0257 0253 D6 07 SUB $07 ; But if A-F, take it down some more +0258 0255 07 BCTOA1 RLCA ; Rotate the nybble from low to high +0259 0256 07 RLCA ; One bit at a time +0260 0257 07 RLCA ; Until we +0261 0258 07 RLCA ; Get there with it +0262 0259 47 LD B,A ; Save the converted high nybble +0263 025A 79 LD A,C ; Now get the low order byte +0264 025B D6 30 SUB $30 ; Convert it down from Ascii +0265 025D FE 0A CP $0A ; 0-9 at this point? +0266 025F 38 02 JR C,BCTOA2 ; Good enough then, but +0267 0261 D6 07 SUB $07 ; Take off 7 more if it's A-F +0268 0263 80 BCTOA2 ADD A,B ; Add in the high order nybble +0269 0264 C9 RET +0270 0265 +0271 0265 ; Change Hex in A to actual value in A +0272 0265 D6 30 HEX2VAL SUB $30 +0273 0267 FE 0A CP $0A +0274 0269 D8 RET C +0275 026A D6 07 SUB $07 +0276 026C C9 RET +0277 026D +0278 026D +0279 026D 00 buffPos .DB 0H +0280 026E 00 00 buffPtr .DW 0000H +0281 0270 00 printCount .DB 0H +0282 0271 00 checkSum .DB 0H +0283 0272 00 byteCount .DB 0H +0284 0273 4F 4B 24 OKMess .BYTE "OK$" +0285 0276 3D3D3D3D3D3DchkErrMess .BYTE "======Checksum Error======$" +0285 027C 436865636B73756D204572726F723D3D3D3D3D3D24 +0286 0291 3D3D3D3D3D3DcountErrMess .BYTE "======File Length Error======$" +0286 0297 46696C65204C656E677468204572726F723D3D3D3D3D3D24 +0287 02AF .END +tasm: Number of errors = 0 diff --git a/Z80 CPM and bootloader (basmon)/source/FORM128.LST b/Z80 CPM and bootloader (basmon)/source/FORM128.LST new file mode 100644 index 0000000..fe9ca64 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/FORM128.LST @@ -0,0 +1,230 @@ +0001 0000 ;================================================================================== +0002 0000 ; Contents of this file are copyright Grant Searle +0003 0000 ; +0004 0000 ; You have permission to use this for NON COMMERCIAL USE ONLY +0005 0000 ; If you wish to use it elsewhere, please include an acknowledgement to myself. +0006 0000 ; +0007 0000 ; http://searle.hostei.com/grant/index.html +0008 0000 ; +0009 0000 ; eMail: home.micros01@btinternet.com +0010 0000 ; +0011 0000 ; If the above don't work, please perform an Internet search to see if I have +0012 0000 ; updated the web page hosting service. +0013 0000 ; +0014 0000 ;================================================================================== +0015 0000 +0016 0000 numDrives .EQU 15 ; Not including A: +0017 0000 +0018 0000 +0019 0000 SD_DATA .EQU 088H +0020 0000 SD_CONTROL .EQU 089H +0021 0000 SD_STATUS .EQU 089H +0022 0000 SD_LBA0 .EQU 08AH +0023 0000 SD_LBA1 .EQU 08BH +0024 0000 SD_LBA2 .EQU 08CH +0025 0000 +0026 0000 LF .EQU 0AH ;line feed +0027 0000 FF .EQU 0CH ;form feed +0028 0000 CR .EQU 0DH ;carriage RETurn +0029 0000 +0030 0000 ;==================================================================================== +0031 0000 +0032 5000 .ORG 5000H ; Format program origin. +0033 5000 +0034 5000 +0035 5000 CD D5 50 CALL printInline +0036 5003 43502F4D2046 .TEXT "CP/M Formatter 2.0 by G. Searle 2013" +0036 5009 6F726D617474657220322E3020627920472E20536561726C652032303133 +0037 5027 0D 0A 00 .DB CR,LF,0 +0038 502A +0039 502A 3E 41 LD A,'A' +0040 502C 32 E7 50 LD (drvName),A +0041 502F +0042 502F ; There are 512 directory entries per disk, 4 DIR entries per sector +0043 502F ; So 128 x 128 byte sectors are to be initialised +0044 502F ; The drive uses 512 byte sectors, so 32 x 512 byte sectors per disk +0045 502F ; require initialisation +0046 502F +0047 502F ;Drive 0 (A:) is slightly different due to reserved track, so DIR sector starts at 32 +0048 502F 3A E7 50 LD A,(drvName) +0049 5032 CF RST 08H ; Print drive letter +0050 5033 3C INC A +0051 5034 32 E7 50 LD (drvName),A +0052 5037 +0053 5037 3E 20 LD A,$20 +0054 5039 32 E6 50 LD (secNo),A +0055 503C +0056 503C processSectorA: +0057 503C +0058 503C 3A E6 50 LD A,(secNo) +0059 503F D3 8A OUT (SD_LBA0),A +0060 5041 3E 00 LD A,0 +0061 5043 D3 8B OUT (SD_LBA1),A +0062 5045 3E 00 LD A,0 +0063 5047 D3 8C OUT (SD_LBA2),A +0064 5049 3E E0 LD a,$E0 +0065 504B +0066 504B CD A7 50 call writehst +0067 504E +0068 504E 3A E6 50 LD A,(secNo) +0069 5051 3C INC A +0070 5052 32 E6 50 LD (secNo),A +0071 5055 FE 40 CP $40 +0072 5057 20 E3 JR NZ, processSectorA +0073 5059 +0074 5059 +0075 5059 +0076 5059 ;Drive 1 onwards (B: etc) don't have reserved tracks, so sector starts at 0 +0077 5059 +0078 5059 11 40 00 LD DE,$0040 ; HL increment +0079 505C 21 40 00 LD HL,$0040 ; H = LBA2, L=LBA1, initialise for drive 1 (B:) +0080 505F +0081 505F 06 0F LD B,numDrives +0082 5061 +0083 5061 processDirs: +0084 5061 +0085 5061 3A E7 50 LD A,(drvName) +0086 5064 CF RST 08H ; Print drive letter +0087 5065 3C INC A +0088 5066 32 E7 50 LD (drvName),A +0089 5069 +0090 5069 3E 00 LD A,0 +0091 506B 32 E6 50 LD (secNo),A +0092 506E +0093 506E processSector: +0094 506E 3A E6 50 LD A,(secNo) +0095 5071 D3 8A OUT (SD_LBA0),A +0096 5073 7D LD A,L +0097 5074 D3 8B OUT (SD_LBA1),A +0098 5076 7C LD A,H +0099 5077 D3 8C OUT (SD_LBA2),A +0100 5079 +0101 5079 CD A7 50 call writehst +0102 507C +0103 507C 3A E6 50 LD A,(secNo) +0104 507F 3C INC A +0105 5080 32 E6 50 LD (secNo),A +0106 5083 FE 20 CP $20 +0107 5085 20 E7 JR NZ, processSector +0108 5087 +0109 5087 19 ADD HL,DE +0110 5088 +0111 5088 05 DEC B +0112 5089 20 D6 JR NZ,processDirs +0113 508B +0114 508B CD D5 50 CALL printInline +0115 508E 0D 0A .DB CR,LF +0116 5090 466F726D6174 .TEXT "Formatting complete" +0116 5096 74696E6720636F6D706C657465 +0117 50A3 0D 0A 00 .DB CR,LF,0 +0118 50A6 +0119 50A6 C9 RET +0120 50A7 +0121 50A7 ;================================================================================================ +0122 50A7 ; Write physical sector to host +0123 50A7 ;================================================================================================ +0124 50A7 +0125 50A7 writehst: +0126 50A7 F5 PUSH AF +0127 50A8 C5 PUSH BC +0128 50A9 E5 PUSH HL +0129 50AA +0130 50AA DB 89 wrWait1: IN A,(SD_STATUS) +0131 50AC FE 80 CP 128 +0132 50AE 20 FA JR NZ,wrWait1 +0133 50B0 +0134 50B0 ;CALL setLBAaddr +0135 50B0 +0136 50B0 3E 01 LD A,$01 ; 01 = Write block +0137 50B2 D3 89 OUT (SD_CONTROL),A +0138 50B4 +0139 50B4 0E 04 LD c,4 +0140 50B6 wr4secs: +0141 50B6 21 E8 50 LD HL,dirData +0142 50B9 06 80 LD b,128 +0143 50BB wrByte: +0144 50BB DB 89 wrWait2: IN A,(SD_STATUS) +0145 50BD FE A0 CP 160 ; Write buffer empty +0146 50BF 20 FA JR NZ,wrWait2 +0147 50C1 +0148 50C1 ;LD A,'.' +0149 50C1 ;RST 08H +0150 50C1 +0151 50C1 ; UPDATE S0urceror, inserted wait cycle between IN and OUT +0152 50C1 ; to resolve unknown write issue in sd_controller.vhd in combination +0153 50C1 ; with MISTer virtual SD interface sys/sd_card.sv +0154 50C1 ; which results in hangs or write errors. +0155 50C1 C5 push bc +0156 50C2 06 64 ld b,100 +0157 50C4 _again: +0158 50C4 10 FE djnz _again +0159 50C6 C1 pop bc +0160 50C7 ; END UPDATE +0161 50C7 +0162 50C7 7E LD A,(HL) +0163 50C8 D3 88 OUT (SD_DATA),A +0164 50CA +0165 50CA 23 INC HL +0166 50CB 05 dec b +0167 50CC 20 ED JR NZ, wrByte +0168 50CE +0169 50CE 0D dec c +0170 50CF 20 E5 JR NZ,wr4secs +0171 50D1 +0172 50D1 E1 POP HL +0173 50D2 C1 POP BC +0174 50D3 F1 POP AF +0175 50D4 +0176 50D4 ;XOR a +0177 50D4 ;ld (erflag),a +0178 50D4 C9 RET +0179 50D5 +0180 50D5 ;================================================================================================ +0181 50D5 ; Utilities +0182 50D5 ;================================================================================================ +0183 50D5 +0184 50D5 printInline: +0185 50D5 E3 EX (SP),HL ; PUSH HL and put RET ADDress into HL +0186 50D6 F5 PUSH AF +0187 50D7 C5 PUSH BC +0188 50D8 7E nextILChar: LD A,(HL) +0189 50D9 FE 00 CP 0 +0190 50DB 28 04 JR Z,endOfPrint +0191 50DD CF RST 08H +0192 50DE 23 INC HL +0193 50DF 18 F7 JR nextILChar +0194 50E1 23 endOfPrint: INC HL ; Get past "null" terminator +0195 50E2 C1 POP BC +0196 50E3 F1 POP AF +0197 50E4 E3 EX (SP),HL ; PUSH new RET ADDress on stack and restore HL +0198 50E5 C9 RET +0199 50E6 +0200 50E6 +0201 50E6 00 secNo .db 0 +0202 50E7 00 drvName .db 0 +0203 50E8 +0204 50E8 +0205 50E8 ; Directory data for 1 x 128 byte sector +0206 50E8 dirData: +0207 50E8 E52020202020 .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 +0207 50EE 20202020202000000000 +0208 50F8 000000000000 .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +0208 50FE 00000000000000000000 +0209 5108 +0210 5108 E52020202020 .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 +0210 510E 20202020202000000000 +0211 5118 000000000000 .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +0211 511E 00000000000000000000 +0212 5128 +0213 5128 E52020202020 .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 +0213 512E 20202020202000000000 +0214 5138 000000000000 .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +0214 513E 00000000000000000000 +0215 5148 +0216 5148 E52020202020 .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 +0216 514E 20202020202000000000 +0217 5158 000000000000 .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +0217 515E 00000000000000000000 +0218 5168 +0219 5168 .END +tasm: Number of errors = 0 diff --git a/Z80 CPM and bootloader (basmon)/source/PUTSYS.LST b/Z80 CPM and bootloader (basmon)/source/PUTSYS.LST new file mode 100644 index 0000000..b1100a3 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/PUTSYS.LST @@ -0,0 +1,166 @@ +0001 0000 ;================================================================================== +0002 0000 ; Contents of this file are copyright Grant Searle +0003 0000 ; +0004 0000 ; You have permission to use this for NON COMMERCIAL USE ONLY +0005 0000 ; If you wish to use it elsewhere, please include an acknowledgement to myself. +0006 0000 ; +0007 0000 ; http://searle.hostei.com/grant/index.html +0008 0000 ; +0009 0000 ; eMail: home.micros01@btinternet.com +0010 0000 ; +0011 0000 ; If the above don't work, please perform an Internet search to see if I have +0012 0000 ; updated the web page hosting service. +0013 0000 ; +0014 0000 ;================================================================================== +0015 0000 +0016 0000 loadAddr .EQU 0D000h +0017 0000 numSecs .EQU 24 ; Number of 512 sectors to be loaded +0018 0000 +0019 0000 SD_DATA .EQU 088H +0020 0000 SD_CONTROL .EQU 089H +0021 0000 SD_STATUS .EQU 089H +0022 0000 SD_LBA0 .EQU 08AH +0023 0000 SD_LBA1 .EQU 08BH +0024 0000 SD_LBA2 .EQU 08CH +0025 0000 +0026 0000 LF .EQU 0AH ;line feed +0027 0000 FF .EQU 0CH ;form feed +0028 0000 CR .EQU 0DH ;carriage RETurn +0029 0000 +0030 0000 ;================================================================================================ +0031 0000 +0032 5000 .ORG 5000H ; Loader origin. +0033 5000 +0034 5000 CD BA 50 CALL printInline +0035 5003 43502F4D2053 .TEXT "CP/M System Transfer by G. Searle 2012-13" +0035 5009 797374656D205472616E7366657220627920472E20536561726C6520323031322D3133 +0036 502C 0D 0A 00 .DB CR,LF,0 +0037 502F +0038 502F 06 18 LD B,numSecs +0039 5031 +0040 5031 3E 00 LD A,0 +0041 5033 32 CB 50 LD (lba0),A +0042 5036 32 CC 50 ld (lba1),A +0043 5039 32 CD 50 ld (lba2),A +0044 503C 32 CE 50 ld (lba3),A +0045 503F 21 00 D0 LD HL,loadAddr +0046 5042 22 CF 50 LD (dmaAddr),HL +0047 5045 processSectors: +0048 5045 +0049 5045 CD 8C 50 call writehst +0050 5048 +0051 5048 11 00 02 LD DE,0200H +0052 504B 2A CF 50 LD HL,(dmaAddr) +0053 504E 19 ADD HL,DE +0054 504F 22 CF 50 LD (dmaAddr),HL +0055 5052 3A CB 50 LD A,(lba0) +0056 5055 3C INC A +0057 5056 32 CB 50 LD (lba0),A +0058 5059 +0059 5059 10 EA djnz processSectors +0060 505B +0061 505B CD BA 50 CALL printInline +0062 505E 0D 0A .DB CR,LF +0063 5060 53797374656D .TEXT "System transfer complete" +0063 5066 207472616E7366657220636F6D706C657465 +0064 5078 0D 0A 00 .DB CR,LF,0 +0065 507B +0066 507B C9 RET +0067 507C +0068 507C ; ========================================================================= +0069 507C ; Disk routines as used in CBIOS +0070 507C ; ========================================================================= +0071 507C setLBAaddr: +0072 507C 3A CD 50 LD A,(lba2) +0073 507F D3 8C OUT (SD_LBA2),A +0074 5081 3A CC 50 LD A,(lba1) +0075 5084 D3 8B OUT (SD_LBA1),A +0076 5086 3A CB 50 LD A,(lba0) +0077 5089 D3 8A OUT (SD_LBA0),A +0078 508B C9 ret +0079 508C +0080 508C ;================================================================================================ +0081 508C ; Write physical sector to host +0082 508C ;================================================================================================ +0083 508C +0084 508C writehst: +0085 508C F5 PUSH AF +0086 508D C5 PUSH BC +0087 508E E5 PUSH HL +0088 508F +0089 508F DB 89 wrWait1: IN A,(SD_STATUS) +0090 5091 FE 80 CP 128 +0091 5093 20 FA JR NZ,wrWait1 +0092 5095 +0093 5095 CD 7C 50 CALL setLBAaddr +0094 5098 +0095 5098 3E 01 LD A,$01 ; 01 = Write block +0096 509A D3 89 OUT (SD_CONTROL),A +0097 509C +0098 509C 0E 04 LD c,4 +0099 509E ;LD HL,hstbuf +0100 509E wr4secs: +0101 509E 06 80 LD b,128 +0102 50A0 wrByte: +0103 50A0 +0104 50A0 DB 89 wrWait2: IN A,(SD_STATUS) +0105 50A2 FE A0 CP 160 ; Write buffer empty +0106 50A4 20 FA JR NZ,wrWait2 +0107 50A6 +0108 50A6 ; UPDATE S0urceror, inserted wait cycle between IN and OUT +0109 50A6 ; to resolve unknown write issue in sd_controller.vhd in combination +0110 50A6 ; with MISTer virtual SD interface sys/sd_card.sv +0111 50A6 ; which results in hangs or write errors. +0112 50A6 C5 push bc +0113 50A7 06 64 ld b,100 +0114 50A9 _again: +0115 50A9 10 FE djnz _again +0116 50AB C1 pop bc +0117 50AC ; END UPDATE +0118 50AC +0119 50AC 7E LD A,(HL) +0120 50AD D3 88 OUT (SD_DATA),A +0121 50AF 23 INC HL +0122 50B0 05 dec b +0123 50B1 20 ED JR NZ, wrByte +0124 50B3 +0125 50B3 0D dec c +0126 50B4 20 E8 JR NZ,wr4secs +0127 50B6 +0128 50B6 E1 POP HL +0129 50B7 C1 POP BC +0130 50B8 F1 POP AF +0131 50B9 +0132 50B9 ;XOR a +0133 50B9 ;ld (erflag),a +0134 50B9 C9 RET +0135 50BA +0136 50BA +0137 50BA ;================================================================================================ +0138 50BA ; Utilities +0139 50BA ;================================================================================================ +0140 50BA +0141 50BA printInline: +0142 50BA E3 EX (SP),HL ; PUSH HL and put RET ADDress into HL +0143 50BB F5 PUSH AF +0144 50BC C5 PUSH BC +0145 50BD 7E nextILChar: LD A,(HL) +0146 50BE FE 00 CP 0 +0147 50C0 28 04 JR Z,endOfPrint +0148 50C2 CF RST 08H +0149 50C3 23 INC HL +0150 50C4 18 F7 JR nextILChar +0151 50C6 23 endOfPrint: INC HL ; Get past "null" terminator +0152 50C7 C1 POP BC +0153 50C8 F1 POP AF +0154 50C9 E3 EX (SP),HL ; PUSH new RET ADDress on stack and restore HL +0155 50CA C9 RET +0156 50CB +0157 50CB 00 lba0 .DB 00h +0158 50CC 00 lba1 .DB 00h +0159 50CD 00 lba2 .DB 00h +0160 50CE 00 lba3 .DB 00h +0161 50CF 00 00 dmaAddr .dw 0 +0162 50D1 +0163 50D1 .END +tasm: Number of errors = 0 diff --git a/Z80 CPM and bootloader (basmon)/source/basMon.asm b/Z80 CPM and bootloader (basmon)/source/basMon.asm new file mode 100644 index 0000000..fb36b2e --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/basMon.asm @@ -0,0 +1,4992 @@ +;================================================================================== +; The updates to the original BASIC within this file are copyright Grant Searle +; +; You have permission to use this for NON COMMERCIAL USE ONLY +; If you wish to use it elsewhere, please include an acknowledgement to myself. +; +; http://searle.hostei.com/grant/index.html +; +; eMail: home.micros01@btinternet.com +; +; If the above don't work, please perform an Internet search to see if I have +; updated the web page hosting service. +; +;================================================================================== + + +;================================================================================== +; Contents of this file are copyright Grant Searle +; HEX routines from Joel Owens. +; +; You have permission to use this for NON COMMERCIAL USE ONLY +; If you wish to use it elsewhere, please include an acknowledgement to myself. +; +; http://searle.hostei.com/grant/index.html +; +; eMail: home.micros01@btinternet.com +; +; If the above don't work, please perform an Internet search to see if I have +; updated the web page hosting service. +; +;================================================================================== + +;------------------------------------------------------------------------------ +; +; Z80 Monitor Rom +; +;------------------------------------------------------------------------------ +; General Equates +;------------------------------------------------------------------------------ + +;CR .EQU 0DH +;LF .EQU 0AH +;ESC .EQU 1BH +;CTRLC .EQU 03H +M_CLS .EQU 0CH + + +loadAddr .EQU 0D000h ; CP/M load address +numSecs .EQU 24 ; Number of 512 sectors to be loaded + + +RTS_HIGH .EQU 0D5H +RTS_LOW .EQU 095H + +ACIA0_D .EQU $81 +ACIA0_C .EQU $80 +ACIA1_D .EQU $83 +ACIA1_C .EQU $82 + +SD_DATA .EQU 088H +SD_CONTROL .EQU 089H +SD_STATUS .EQU 089H +SD_LBA0 .EQU 08AH +SD_LBA1 .EQU 08BH +SD_LBA2 .EQU 08CH + + .ORG $3000 + +primaryIO .ds 1 +secNo .ds 1 +dmaAddr .ds 2 + +lba0 .DB 00h +lba1 .DB 00h +lba2 .DB 00h +lba3 .DB 00h + +stackSpace .ds 32 +M_STACK .EQU $ ; Stack top + + +;------------------------------------------------------------------------------ +; START OF MONITOR ROM +;------------------------------------------------------------------------------ + +MON .ORG $0000 ; MONITOR ROM RESET VECTOR +;------------------------------------------------------------------------------ +; Reset +;------------------------------------------------------------------------------ +RST00 DI ;Disable INTerrupts + JP M_INIT ;Initialize Hardware and go + NOP + NOP + NOP + NOP +;------------------------------------------------------------------------------ +; TX a character over RS232 wait for TXDONE first. +;------------------------------------------------------------------------------ +RST08 JP conout + NOP + NOP + NOP + NOP + NOP +;------------------------------------------------------------------------------ +; RX a character from buffer wait until char ready. +;------------------------------------------------------------------------------ +RST10 JP conin + NOP + NOP + NOP + NOP + NOP +;------------------------------------------------------------------------------ +; Check input buffer status +;------------------------------------------------------------------------------ +RST18 JP CKINCHAR + + +;------------------------------------------------------------------------------ +; Console input routine +; Use the "primaryIO" flag to determine which input port to monitor. +;------------------------------------------------------------------------------ +conin: + LD A,(primaryIO) + CP 0 + JR NZ,coninB +coninA: + +waitForCharA: + call ckincharA + JR Z, waitForCharA + IN A,(ACIA0_D) + RET ; Char ready in A + +coninB: + +waitForCharB: + call ckincharB + JR Z, waitForCharB + IN A,(ACIA1_D) + RET ; Char ready in A + +;------------------------------------------------------------------------------ +; Console output routine +; Use the "primaryIO" flag to determine which output port to send a character. +;------------------------------------------------------------------------------ +conout: PUSH AF ; Store character + LD A,(primaryIO) + CP 0 + JR NZ,conoutB1 + JR conoutA1 +conoutA: + PUSH AF + +conoutA1: CALL CKACIA0 ; See if ACIA channel A is finished transmitting + JR Z,conoutA1 ; Loop until ACIA flag signals ready + POP AF ; RETrieve character + OUT (ACIA0_D),A ; OUTput the character + RET + +conoutB: + PUSH AF + +conoutB1: CALL CKACIA1 ; See if ACIA channel B is finished transmitting + JR Z,conoutB1 ; Loop until ACIA flag signals ready + POP AF ; RETrieve character + OUT (ACIA1_D),A ; OUTput the character + RET + +;------------------------------------------------------------------------------ +; I/O status check routine +; Use the "primaryIO" flag to determine which port to check. +;------------------------------------------------------------------------------ +CKACIA0 + IN A,(ACIA0_C) ; Status byte D1=TX Buff Empty, D0=RX char ready + RRCA ; Rotates RX status into Carry Flag, + BIT 0,A ; Set Zero flag if still transmitting character + RET + +CKACIA1 + IN A,(ACIA1_C) ; Status byte D1=TX Buff Empty, D0=RX char ready + RRCA ; Rotates RX status into Carry Flag, + BIT 0,A ; Set Zero flag if still transmitting character + RET + +;------------------------------------------------------------------------------ +; Check if there is a character in the input buffer +; Use the "primaryIO" flag to determine which port to check. +;------------------------------------------------------------------------------ +CKINCHAR + LD A,(primaryIO) + CP 0 + JR NZ,ckincharB + +ckincharA: + + IN A,(ACIA0_C) ; Status byte + AND $01 + CP $0 ; Z flag set if no char + RET + +ckincharB: + + IN A,(ACIA1_C) ; Status byte + AND $01 + CP $0 ; Z flag set if no char + RET + +;------------------------------------------------------------------------------ +; Filtered Character I/O +;------------------------------------------------------------------------------ + +RDCHR RST 10H + CP LF + JR Z,RDCHR ; Ignore LF + CP ESC + JR NZ,RDCHR1 + LD A,CTRLC ; Change ESC to CTRL-C +RDCHR1 RET + +WRCHR CP CR + JR Z,WRCRLF ; When CR, write CRLF + CP M_CLS + JR Z,WR ; Allow write of "CLS" + CP ' ' ; Don't write out any other control codes + JR C,NOWR ; ie. < space +WR RST 08H +NOWR RET + +WRCRLF LD A,CR + RST 08H + LD A,LF + RST 08H + LD A,CR + RET + + +;------------------------------------------------------------------------------ +; Initialise hardware and start main loop +;------------------------------------------------------------------------------ +M_INIT LD SP,M_STACK ; Set the Stack Pointer + + LD A,RTS_LOW + OUT (ACIA0_C),A ; Initialise ACIA0 + OUT (ACIA1_C),A ; Initialise ACIA1 + ; Display the "Press space to start" message on both consoles + LD A,$00 + LD (primaryIO),A + LD HL,INITTXT + CALL M_PRINT + LD A,$01 + LD (primaryIO),A + LD HL,INITTXT + CALL M_PRINT + + ; Wait until space is in one of the buffers to determine the active console + +waitForSpace: + + CALL ckincharA + jr Z,notInA + LD A,$00 + LD (primaryIO),A + CALL conin + CP ' ' + JP NZ, waitForSpace + JR spacePressed + +notInA: + CALL ckincharB + JR Z,waitForSpace + LD A,$01 + LD (primaryIO),A + CALL conin + CP ' ' + JP NZ, waitForSpace + JR spacePressed + +spacePressed: + + ; Clear message on both consoles + LD A,$0C + CALL conoutA + CALL conoutB + + ; primaryIO is now set to the channel where SPACE was pressed + + + CALL TXCRLF ; TXCRLF + LD HL,M_SIGNON ; Print SIGNON message + CALL M_PRINT + +;------------------------------------------------------------------------------ +; Monitor command loop +;------------------------------------------------------------------------------ +MAIN LD HL,MAIN ; Save entry point for Monitor + PUSH HL ; This is the return address +MAIN0 CALL TXCRLF ; Entry point for Monitor, Normal + LD A,'>' ; Get a ">" + RST 08H ; print it + +MAIN1 CALL RDCHR ; Get a character from the input port + CP ' ' ; or less? + JR C,MAIN1 ; Go back + + CP ':' ; ":"? + JP Z,LOAD ; First character of a HEX load + + CALL WRCHR ; Print char on console + + AND $5F ; Make character uppercase + + CP 'B' + JP Z,BASIC + + CP 'G' + JP Z,M_GOTO + + CP 'X' + JP Z,CPMLOAD + + LD A,'?' ; Get a "?" + RST 08H ; Print it + JR MAIN0 + +;------------------------------------------------------------------------------ +; Print string of characters to Serial A until byte=$00, WITH CR, LF +;------------------------------------------------------------------------------ +M_PRINT LD A,(HL) ; Get character + OR A ; Is it $00 ? + RET Z ; Then RETurn on terminator + RST 08H ; Print it + INC HL ; Next Character + JR M_PRINT ; Continue until $00 + + +TXCRLF LD A,$0D ; + RST 08H ; Print character + LD A,$0A ; + RST 08H ; Print character + RET + +;------------------------------------------------------------------------------ +; Get a character from the console, must be $20-$7F to be valid (no control characters) +; and breaks with the Zero Flag set +;------------------------------------------------------------------------------ +M_GETCHR CALL RDCHR ; RX a Character + CP $03 ; User break? + RET Z + CP $20 ; or better? + JR C,M_GETCHR ; Do it again until we get something usable + RET +;------------------------------------------------------------------------------ +; Gets two ASCII characters from the console (assuming them to be HEX 0-9 A-F) +; Moves them into B and C, converts them into a byte value in A and updates a +; Checksum value in E +;------------------------------------------------------------------------------ +GET2 CALL M_GETCHR ; Get us a valid character to work with + LD B,A ; Load it in B + CALL M_GETCHR ; Get us another character + LD C,A ; load it in C + CALL BCTOA ; Convert ASCII to byte + LD C,A ; Build the checksum + LD A,E + SUB C ; The checksum should always equal zero when checked + LD E,A ; Save the checksum back where it came from + LD A,C ; Retrieve the byte and go back + RET +;------------------------------------------------------------------------------ +; Gets four Hex characters from the console, converts them to values in HL +;------------------------------------------------------------------------------ +GETHL LD HL,$0000 ; Gets xxxx but sets Carry Flag on any Terminator + CALL ECHO ; RX a Character + CP $0D ; ? + JR NZ,GETX2 ; other key +SETCY SCF ; Set Carry Flag + RET ; and Return to main program +;------------------------------------------------------------------------------ +; This routine converts last four hex characters (0-9 A-F) user types into a value in HL +; Rotates the old out and replaces with the new until the user hits a terminating character +;------------------------------------------------------------------------------ +GETX LD HL,$0000 ; CLEAR HL +GETX1 CALL ECHO ; RX a character from the console + CP $0D ; + RET Z ; quit + CP $2C ; <,> can be used to safely quit for multiple entries + RET Z ; (Like filling both DE and HL from the user) +GETX2 CP $03 ; Likewise, a will terminate clean, too, but + JR Z,SETCY ; It also sets the Carry Flag for testing later. + ADD HL,HL ; Otherwise, rotate the previous low nibble to high + ADD HL,HL ; rather slowly + ADD HL,HL ; until we get to the top + ADD HL,HL ; and then we can continue on. + SUB $30 ; Convert ASCII to byte value + CP $0A ; Are we in the 0-9 range? + JR C,GETX3 ; Then we just need to sub $30, but if it is A-F + SUB $07 ; We need to take off 7 more to get the value down to +GETX3 AND $0F ; to the right hex value + ADD A,L ; Add the high nibble to the low + LD L,A ; Move the byte back to A + JR GETX1 ; and go back for next character until he terminates +;------------------------------------------------------------------------------ +; Convert ASCII characters in B C registers to a byte value in A +;------------------------------------------------------------------------------ +BCTOA LD A,B ; Move the hi order byte to A + SUB $30 ; Take it down from Ascii + CP $0A ; Are we in the 0-9 range here? + JR C,BCTOA1 ; If so, get the next nybble + SUB $07 ; But if A-F, take it down some more +BCTOA1 RLCA ; Rotate the nybble from low to high + RLCA ; One bit at a time + RLCA ; Until we + RLCA ; Get there with it + LD B,A ; Save the converted high nybble + LD A,C ; Now get the low order byte + SUB $30 ; Convert it down from Ascii + CP $0A ; 0-9 at this point? + JR C,BCTOA2 ; Good enough then, but + SUB $07 ; Take off 7 more if it's A-F +BCTOA2 ADD A,B ; Add in the high order nybble + RET + +;------------------------------------------------------------------------------ +; Get a character and echo it back to the user +;------------------------------------------------------------------------------ +ECHO CALL RDCHR + CALL WRCHR + RET + +;------------------------------------------------------------------------------ +; GOTO command +;------------------------------------------------------------------------------ +M_GOTO CALL GETHL ; ENTRY POINT FOR oto addr. Get XXXX from user. + RET C ; Return if invalid + PUSH HL + RET ; Jump to HL address value + +;------------------------------------------------------------------------------ +; LOAD Intel Hex format file from the console. +; [Intel Hex Format is: +; 1) Colon (Frame 0) +; 2) Record Length Field (Frames 1 and 2) +; 3) Load Address Field (Frames 3,4,5,6) +; 4) Record Type Field (Frames 7 and 8) +; 5) Data Field (Frames 9 to 9+2*(Record Length)-1 +; 6) Checksum Field - Sum of all byte values from Record Length to and +; including Checksum Field = 0 ] +;------------------------------------------------------------------------------ +LOAD LD E,0 ; First two Characters is the Record Length Field + CALL GET2 ; Get us two characters into BC, convert it to a byte + LD D,A ; Load Record Length count into D + CALL GET2 ; Get next two characters, Memory Load Address + LD H,A ; put value in H register. + CALL GET2 ; Get next two characters, Memory Load Address + LD L,A ; put value in L register. + CALL GET2 ; Get next two characters, Record Field Type + CP $01 ; Record Field Type 00 is Data, 01 is End of File + JR NZ,LOAD2 ; Must be the end of that file + CALL GET2 ; Get next two characters, assemble into byte + LD A,E ; Recall the Checksum byte + AND A ; Is it Zero? + JR Z,LOAD00 ; Print footer reached message + JR LOADERR ; Checksums don't add up, Error out + +LOAD2 LD A,D ; Retrieve line character counter + AND A ; Are we done with this line? + JR Z,LOAD3 ; Get two more ascii characters, build a byte and checksum + CALL GET2 ; Get next two chars, convert to byte in A, checksum it + LD (HL),A ; Move converted byte in A to memory location + INC HL ; Increment pointer to next memory location + LD A,'.' ; Print out a "." for every byte loaded + RST 08H ; + DEC D ; Decrement line character counter + JR LOAD2 ; and keep loading into memory until line is complete + +LOAD3 CALL GET2 ; Get two chars, build byte and checksum + LD A,E ; Check the checksum value + AND A ; Is it zero? + RET Z + +LOADERR LD HL,CKSUMERR ; Get "Checksum Error" message + CALL M_PRINT ; Print Message from (HL) and terminate the load + RET + +LOAD00 LD HL,LDETXT ; Print load complete message + CALL M_PRINT + RET + +;------------------------------------------------------------------------------ +; Start BASIC command +;------------------------------------------------------------------------------ +BASIC + LD HL,M_BASTXT + CALL M_PRINT + CALL M_GETCHR + RET Z ; Cancel if CTRL-C + AND $5F ; uppercase + CP 'C' + JP Z,COLD + CP 'W' + JP Z,WARM + RET + +;------------------------------------------------------------------------------ +; CP/M load command +;------------------------------------------------------------------------------ +CPMLOAD + + LD HL,CPMTXT + CALL M_PRINT + CALL M_GETCHR + RET Z ; Cancel if CTRL-C + AND $5F ; uppercase + CP 'Y' + JP Z,CPMLOAD2 + RET +CPMTXT + .BYTE $0D,$0A + .TEXT "Boot CP/M?" + .BYTE $00 + +CPMTXT2 + .BYTE $0D,$0A + .TEXT "Loading CP/M..." + .BYTE $0D,$0A,$00 + +CPMLOAD2 + LD HL,CPMTXT2 + CALL M_PRINT + + LD B,numSecs + + LD A,0 + LD (lba0),A + ld (lba1),A + ld (lba2),A + ld (lba3),A + + LD HL,loadAddr + LD (dmaAddr),HL +processSectors: + + call readhst + + LD DE,0200H + LD HL,(dmaAddr) + ADD HL,DE + LD (dmaAddr),HL + LD A,(lba0) + INC A + LD (lba0),A + + djnz processSectors + +; Start CP/M using entry at top of BIOS +; The current active console stream ID is pushed onto the stack +; to allow the CBIOS to pick it up +; 0 = ACIA0, 1 = ACIA1 + + ld A,(primaryIO) + PUSH AF + ld HL,($FFFE) + jp (HL) + + +;------------------------------------------------------------------------------ +; ROUTINES AS USED IN BIOS +;------------------------------------------------------------------------------ + +;================================================================================================ +; Convert track/head/sector into LBA for physical access to the disk +;================================================================================================ +setLBAaddr: + ; Transfer LBA to disk (LBA3 not used on SD card) + LD A,(lba2) + OUT (SD_LBA2),A + LD A,(lba1) + OUT (SD_LBA1),A + LD A,(lba0) + OUT (SD_LBA0),A + RET + +;================================================================================================ +; Read physical sector from host +;================================================================================================ + +readhst: + PUSH AF + PUSH BC + PUSH HL + +rdWait1: IN A,(SD_STATUS) + CP 128 + JR NZ,rdWait1 + + CALL setLBAaddr + + LD A,$00 ; 00 = Read block + OUT (SD_CONTROL),A + + LD c,4 +; LD HL,hstbuf +rd4secs: + LD b,128 +rdByte: + +rdWait2: IN A,(SD_STATUS) + CP 224 ; Read byte waiting + JR NZ,rdWait2 + + IN A,(SD_DATA) + + LD (HL),A + INC HL + dec b + JR NZ, rdByte + dec c + JR NZ,rd4secs + + POP HL + POP BC + POP AF + +; XOR a +; ld (erflag),a + RET + +;------------------------------------------------------------------------------ +; END OF ROUTINES AS USED IN BIOS +;------------------------------------------------------------------------------ + + +M_SIGNON .BYTE "CP/M Boot ROM 2.0" + .BYTE " by G. Searle" + .BYTE $0D,$0A + .BYTE $0D,$0A + .TEXT "BC or BW - ROM BASIC Cold/Warm" + .BYTE $0D,$0A + .TEXT "X - Boot CP/M (load $D000-$FFFF)" + .BYTE $0D,$0A + .TEXT ":nnnn... - Load Intel-Hex file record" + .BYTE $0D,$0A + .TEXT "Gnnnn - Run loc nnnn" + .BYTE $0D,$0A + .BYTE $00 + +M_BASTXT + .BYTE $0D,$0A + .TEXT "Cold or warm?" + .BYTE $0D,$0A,$00 + +CKSUMERR .BYTE "Checksum error" + .BYTE $0D,$0A,$00 + +INITTXT + .BYTE $0C + .TEXT "Press [SPACE] to activate console" + .BYTE $0D,$0A, $00 + +LDETXT + .TEXT "Complete" + .BYTE $0D,$0A, $00 + +;=========================================================================================================================== + +; NASCOM ROM BASIC Ver 4.7, (C) 1978 Microsoft +; Scanned from source published in 80-BUS NEWS from Vol 2, Issue 3 +; (May-June 1983) to Vol 3, Issue 3 (May-June 1984) +; Adapted for the freeware Zilog Macro Assembler 2.10 to produce +; the original ROM code (checksum A934H). PA + +; GENERAL EQUATES + +CTRLC .EQU 03H ; Control "C" +CTRLG .EQU 07H ; Control "G" +BKSP .EQU 08H ; Back space +LF .EQU 0AH ; Line feed +CS .EQU 0CH ; Clear screen +CR .EQU 0DH ; Carriage return +CTRLO .EQU 0FH ; Control "O" +CTRLQ .EQU 11H ; Control "Q" +CTRLR .EQU 12H ; Control "R" +CTRLS .EQU 13H ; Control "S" +CTRLU .EQU 15H ; Control "U" +ESC .EQU 1BH ; Escape +DEL .EQU 7FH ; Delete + +; BASIC WORK SPACE LOCATIONS + +WRKSPC .EQU 30B0H ; BASIC Work space +USR .EQU WRKSPC+3H ; "USR (x)" jump +OUTSUB .EQU WRKSPC+6H ; "OUT p,n" +OTPORT .EQU WRKSPC+7H ; Port (p) +DIVSUP .EQU WRKSPC+9H ; Division support routine +DIV1 .EQU WRKSPC+0AH ; <- Values +DIV2 .EQU WRKSPC+0EH ; <- to +DIV3 .EQU WRKSPC+12H ; <- be +DIV4 .EQU WRKSPC+15H ; <-inserted +SEED .EQU WRKSPC+17H ; Random number seed +LSTRND .EQU WRKSPC+3AH ; Last random number +INPSUB .EQU WRKSPC+3EH ; #INP (x)" Routine +INPORT .EQU WRKSPC+3FH ; PORT (x) +NULLS .EQU WRKSPC+41H ; Number of nulls +LWIDTH .EQU WRKSPC+42H ; Terminal width +COMMAN .EQU WRKSPC+43H ; Width for commas +NULFLG .EQU WRKSPC+44H ; Null after input byte flag +CTLOFG .EQU WRKSPC+45H ; Control "O" flag +LINESC .EQU WRKSPC+46H ; Lines counter +LINESN .EQU WRKSPC+48H ; Lines number +CHKSUM .EQU WRKSPC+4AH ; Array load/save check sum +NMIFLG .EQU WRKSPC+4CH ; Flag for NMI break routine +BRKFLG .EQU WRKSPC+4DH ; Break flag +RINPUT .EQU WRKSPC+4EH ; Input reflection +POINT .EQU WRKSPC+51H ; "POINT" reflection (unused) +PSET .EQU WRKSPC+54H ; "SET" reflection +RESET .EQU WRKSPC+57H ; "RESET" reflection +STRSPC .EQU WRKSPC+5AH ; Bottom of string space +LINEAT .EQU WRKSPC+5CH ; Current line number +BASTXT .EQU WRKSPC+5EH ; Pointer to start of program +BUFFER .EQU WRKSPC+61H ; Input buffer +STACK .EQU WRKSPC+66H ; Initial stack +CURPOS .EQU WRKSPC+0ABH ; Character position on line +LCRFLG .EQU WRKSPC+0ACH ; Locate/Create flag +TYPE .EQU WRKSPC+0ADH ; Data type flag +DATFLG .EQU WRKSPC+0AEH ; Literal statement flag +LSTRAM .EQU WRKSPC+0AFH ; Last available RAM +TMSTPT .EQU WRKSPC+0B1H ; Temporary string pointer +TMSTPL .EQU WRKSPC+0B3H ; Temporary string pool +TMPSTR .EQU WRKSPC+0BFH ; Temporary string +STRBOT .EQU WRKSPC+0C3H ; Bottom of string space +CUROPR .EQU WRKSPC+0C5H ; Current operator in EVAL +LOOPST .EQU WRKSPC+0C7H ; First statement of loop +DATLIN .EQU WRKSPC+0C9H ; Line of current DATA item +FORFLG .EQU WRKSPC+0CBH ; "FOR" loop flag +LSTBIN .EQU WRKSPC+0CCH ; Last byte entered +READFG .EQU WRKSPC+0CDH ; Read/Input flag +BRKLIN .EQU WRKSPC+0CEH ; Line of break +NXTOPR .EQU WRKSPC+0D0H ; Next operator in EVAL +ERRLIN .EQU WRKSPC+0D2H ; Line of error +CONTAD .EQU WRKSPC+0D4H ; Where to CONTinue +PROGND .EQU WRKSPC+0D6H ; End of program +VAREND .EQU WRKSPC+0D8H ; End of variables +ARREND .EQU WRKSPC+0DAH ; End of arrays +NXTDAT .EQU WRKSPC+0DCH ; Next data item +FNRGNM .EQU WRKSPC+0DEH ; Name of FN argument +FNARG .EQU WRKSPC+0E0H ; FN argument value +FPREG .EQU WRKSPC+0E4H ; Floating point register +FPEXP .EQU FPREG+3 ; Floating point exponent +SGNRES .EQU WRKSPC+0E8H ; Sign of result +PBUFF .EQU WRKSPC+0E9H ; Number print buffer +MULVAL .EQU WRKSPC+0F6H ; Multiplier +PROGST .EQU WRKSPC+0F9H ; Start of program text area +STLOOK .EQU WRKSPC+15DH ; Start of memory test + +; BASIC ERROR CODE VALUES + +NF .EQU 00H ; NEXT without FOR +SN .EQU 02H ; Syntax error +RG .EQU 04H ; RETURN without GOSUB +OD .EQU 06H ; Out of DATA +FC .EQU 08H ; Function call error +OV .EQU 0AH ; Overflow +OM .EQU 0CH ; Out of memory +UL .EQU 0EH ; Undefined line number +BS .EQU 10H ; Bad subscript +DD .EQU 12H ; Re-DIMensioned array +DZ .EQU 14H ; Division by zero (/0) +ID .EQU 16H ; Illegal direct +TM .EQU 18H ; Type miss-match +OS .EQU 1AH ; Out of string space +LS .EQU 1CH ; String too long +ST .EQU 1EH ; String formula too complex +CN .EQU 20H ; Can't CONTinue +UF .EQU 22H ; UnDEFined FN function +MO .EQU 24H ; Missing operand +HX .EQU 26H ; HEX error +BN .EQU 28H ; BIN error + +; .ORG 00396H + +COLD: JP STARTB ; Jump for cold start +WARM: JP WARMST ; Jump for warm start +STARTB: + LD IX,0 ; Flag cold start + JP CSTART ; Jump to initialise + + .WORD DEINT ; Get integer -32768 to 32767 + .WORD ABPASS ; Return integer in AB + + +CSTART: LD HL,WRKSPC ; Start of workspace RAM + LD SP,HL ; Set up a temporary stack + JP INITST ; Go to initialise + +INIT: LD DE,INITAB ; Initialise workspace + LD B,INITBE-INITAB+3; Bytes to copy + LD HL,WRKSPC ; Into workspace RAM +COPY: LD A,(DE) ; Get source + LD (HL),A ; To destination + INC HL ; Next destination + INC DE ; Next source + DEC B ; Count bytes + JP NZ,COPY ; More to move + LD SP,HL ; Temporary stack + CALL CLREG ; Clear registers and stack + CALL PRNTCRLF ; Output CRLF + LD (BUFFER+72+1),A ; Mark end of buffer + LD (PROGST),A ; Initialise program area +MSIZE: LD HL,MEMMSG ; Point to message + CALL PRS ; Output "Memory size" + CALL PROMPT ; Get input with '?' + CALL GETCHR ; Get next character + OR A ; Set flags + JP NZ,TSTMEM ; If number - Test if RAM there + LD HL,STLOOK ; Point to start of RAM +MLOOP: INC HL ; Next byte + LD A,H ; Above address FFFF ? + OR L + JP Z,SETTOP ; Yes - 64K RAM + LD A,(HL) ; Get contents + LD B,A ; Save it + CPL ; Flip all bits + LD (HL),A ; Put it back + CP (HL) ; RAM there if same + LD (HL),B ; Restore old contents + JP Z,MLOOP ; If RAM - test next byte + JP SETTOP ; Top of RAM found + +TSTMEM: CALL ATOH ; Get high memory into DE + OR A ; Set flags on last byte + JP NZ,SNERR ; ?SN Error if bad character + EX DE,HL ; Address into HL + DEC HL ; Back one byte + LD A,11011001B ; Test byte + LD B,(HL) ; Get old contents + LD (HL),A ; Load test byte + CP (HL) ; RAM there if same + LD (HL),B ; Restore old contents + JP NZ,MSIZE ; Ask again if no RAM + +SETTOP: DEC HL ; Back one byte + LD DE,STLOOK-1 ; See if enough RAM + CALL CPDEHL ; Compare DE with HL + JP C,MSIZE ; Ask again if not enough RAM + LD DE,0-50 ; 50 Bytes string space + LD (LSTRAM),HL ; Save last available RAM + ADD HL,DE ; Allocate string space + LD (STRSPC),HL ; Save string space + CALL CLRPTR ; Clear program area + LD HL,(STRSPC) ; Get end of memory + LD DE,0-17 ; Offset for free bytes + ADD HL,DE ; Adjust HL + LD DE,PROGST ; Start of program text + LD A,L ; Get LSB + SUB E ; Adjust it + LD L,A ; Re-save + LD A,H ; Get MSB + SBC A,D ; Adjust it + LD H,A ; Re-save + PUSH HL ; Save bytes free + LD HL,SIGNON ; Sign-on message + CALL PRS ; Output string + POP HL ; Get bytes free back + CALL PRNTHL ; Output amount of free memory + LD HL,BFREE ; " Bytes free" message + CALL PRS ; Output string + +WARMST: LD SP,STACK ; Temporary stack +BRKRET: CALL CLREG ; Clear registers and stack + JP PRNTOK ; Go to get command line + +BFREE: .BYTE " Bytes free",CR,LF,0,0 + +SIGNON: .BYTE "Z80 BASIC Ver 4.7b",CR,LF + .BYTE "Copyright ",40,"C",41 + .BYTE " 1978 by Microsoft",CR,LF,0,0 + +MEMMSG: .BYTE "Memory top",0 + +; FUNCTION ADDRESS TABLE + +FNCTAB: .WORD SGN + .WORD INT + .WORD ABS + .WORD USR + .WORD FRE + .WORD INP + .WORD POS + .WORD SQR + .WORD RND + .WORD LOG + .WORD EXP + .WORD COS + .WORD SIN + .WORD TAN + .WORD ATN + .WORD PEEK + .WORD DEEK + .WORD POINT + .WORD LEN + .WORD STR + .WORD VAL + .WORD ASC + .WORD CHR + .WORD HEX + .WORD BIN + .WORD LEFT + .WORD RIGHT + .WORD MID + +; RESERVED WORD LIST + +WORDS: .BYTE 'E'+80H,"ND" + .BYTE 'F'+80H,"OR" + .BYTE 'N'+80H,"EXT" + .BYTE 'D'+80H,"ATA" + .BYTE 'I'+80H,"NPUT" + .BYTE 'D'+80H,"IM" + .BYTE 'R'+80H,"EAD" + .BYTE 'L'+80H,"ET" + .BYTE 'G'+80H,"OTO" + .BYTE 'R'+80H,"UN" + .BYTE 'I'+80H,"F" + .BYTE 'R'+80H,"ESTORE" + .BYTE 'G'+80H,"OSUB" + .BYTE 'R'+80H,"ETURN" + .BYTE 'R'+80H,"EM" + .BYTE 'S'+80H,"TOP" + .BYTE 'O'+80H,"UT" + .BYTE 'O'+80H,"N" + .BYTE 'N'+80H,"ULL" + .BYTE 'W'+80H,"AIT" + .BYTE 'D'+80H,"EF" + .BYTE 'P'+80H,"OKE" + .BYTE 'D'+80H,"OKE" + .BYTE 'S'+80H,"CREEN" + .BYTE 'L'+80H,"INES" + .BYTE 'C'+80H,"LS" + .BYTE 'W'+80H,"IDTH" + .BYTE 'M'+80H,"ONITOR" + .BYTE 'S'+80H,"ET" + .BYTE 'R'+80H,"ESET" + .BYTE 'P'+80H,"RINT" + .BYTE 'C'+80H,"ONT" + .BYTE 'L'+80H,"IST" + .BYTE 'C'+80H,"LEAR" + .BYTE 'C'+80H,"LOAD" + .BYTE 'C'+80H,"SAVE" + .BYTE 'N'+80H,"EW" + + .BYTE 'T'+80H,"AB(" + .BYTE 'T'+80H,"O" + .BYTE 'F'+80H,"N" + .BYTE 'S'+80H,"PC(" + .BYTE 'T'+80H,"HEN" + .BYTE 'N'+80H,"OT" + .BYTE 'S'+80H,"TEP" + + .BYTE '+'+80H + .BYTE '-'+80H + .BYTE '*'+80H + .BYTE '/'+80H + .BYTE '^'+80H + .BYTE 'A'+80H,"ND" + .BYTE 'O'+80H,"R" + .BYTE '>'+80H + .BYTE '='+80H + .BYTE '<'+80H + + .BYTE 'S'+80H,"GN" + .BYTE 'I'+80H,"NT" + .BYTE 'A'+80H,"BS" + .BYTE 'U'+80H,"SR" + .BYTE 'F'+80H,"RE" + .BYTE 'I'+80H,"NP" + .BYTE 'P'+80H,"OS" + .BYTE 'S'+80H,"QR" + .BYTE 'R'+80H,"ND" + .BYTE 'L'+80H,"OG" + .BYTE 'E'+80H,"XP" + .BYTE 'C'+80H,"OS" + .BYTE 'S'+80H,"IN" + .BYTE 'T'+80H,"AN" + .BYTE 'A'+80H,"TN" + .BYTE 'P'+80H,"EEK" + .BYTE 'D'+80H,"EEK" + .BYTE 'P'+80H,"OINT" + .BYTE 'L'+80H,"EN" + .BYTE 'S'+80H,"TR$" + .BYTE 'V'+80H,"AL" + .BYTE 'A'+80H,"SC" + .BYTE 'C'+80H,"HR$" + .BYTE 'H'+80H,"EX$" + .BYTE 'B'+80H,"IN$" + .BYTE 'L'+80H,"EFT$" + .BYTE 'R'+80H,"IGHT$" + .BYTE 'M'+80H,"ID$" + .BYTE 80H ; End of list marker + +; KEYWORD ADDRESS TABLE + +WORDTB: .WORD PEND + .WORD FOR + .WORD NEXT + .WORD DATA + .WORD INPUT + .WORD DIM + .WORD READ + .WORD LET + .WORD GOTO + .WORD RUN + .WORD IF + .WORD RESTOR + .WORD GOSUB + .WORD RETURN + .WORD REM + .WORD STOP + .WORD POUT + .WORD ON + .WORD NULL + .WORD WAIT + .WORD DEF + .WORD POKE + .WORD DOKE + .WORD REM + .WORD LINES + .WORD CLS + .WORD WIDTH + .WORD MONITR + .WORD PSET + .WORD RESET + .WORD PRINT + .WORD CONT + .WORD LIST + .WORD CLEAR + .WORD REM + .WORD REM + .WORD NEW + +; RESERVED WORD TOKEN VALUES + +ZEND .EQU 080H ; END +ZFOR .EQU 081H ; FOR +ZDATA .EQU 083H ; DATA +ZGOTO .EQU 088H ; GOTO +ZGOSUB .EQU 08CH ; GOSUB +ZREM .EQU 08EH ; REM +ZPRINT .EQU 09EH ; PRINT +ZNEW .EQU 0A4H ; NEW + +ZTAB .EQU 0A5H ; TAB +ZTO .EQU 0A6H ; TO +ZFN .EQU 0A7H ; FN +ZSPC .EQU 0A8H ; SPC +ZTHEN .EQU 0A9H ; THEN +ZNOT .EQU 0AAH ; NOT +ZSTEP .EQU 0ABH ; STEP + +ZPLUS .EQU 0ACH ; + +ZMINUS .EQU 0ADH ; - +ZTIMES .EQU 0AEH ; * +ZDIV .EQU 0AFH ; / +ZOR .EQU 0B2H ; OR +ZGTR .EQU 0B3H ; > +ZEQUAL .EQU 0B4H ; M +ZLTH .EQU 0B5H ; < +ZSGN .EQU 0B6H ; SGN +ZPOINT .EQU 0C7H ; POINT +ZLEFT .EQU 0CDH +2 ; LEFT$ + +; ARITHMETIC PRECEDENCE TABLE + +PRITAB: .BYTE 79H ; Precedence value + .WORD PADD ; FPREG = + FPREG + + .BYTE 79H ; Precedence value + .WORD PSUB ; FPREG = - FPREG + + .BYTE 7CH ; Precedence value + .WORD MULT ; PPREG = * FPREG + + .BYTE 7CH ; Precedence value + .WORD DIV ; FPREG = / FPREG + + .BYTE 7FH ; Precedence value + .WORD POWER ; FPREG = ^ FPREG + + .BYTE 50H ; Precedence value + .WORD PAND ; FPREG = AND FPREG + + .BYTE 46H ; Precedence value + .WORD POR ; FPREG = OR FPREG + +; BASIC ERROR CODE LIST + +ERRORS: .BYTE "NF" ; NEXT without FOR + .BYTE "SN" ; Syntax error + .BYTE "RG" ; RETURN without GOSUB + .BYTE "OD" ; Out of DATA + .BYTE "FC" ; Illegal function call + .BYTE "OV" ; Overflow error + .BYTE "OM" ; Out of memory + .BYTE "UL" ; Undefined line + .BYTE "BS" ; Bad subscript + .BYTE "DD" ; Re-DIMensioned array + .BYTE "/0" ; Division by zero + .BYTE "ID" ; Illegal direct + .BYTE "TM" ; Type mis-match + .BYTE "OS" ; Out of string space + .BYTE "LS" ; String too long + .BYTE "ST" ; String formula too complex + .BYTE "CN" ; Can't CONTinue + .BYTE "UF" ; Undefined FN function + .BYTE "MO" ; Missing operand + .BYTE "HX" ; HEX error + .BYTE "BN" ; BIN error + +; INITIALISATION TABLE ------------------------------------------------------- + +INITAB: JP WARMST ; Warm start jump + JP FCERR ; "USR (X)" jump (Set to Error) + OUT (0),A ; "OUT p,n" skeleton + RET + SUB 0 ; Division support routine + LD L,A + LD A,H + SBC A,0 + LD H,A + LD A,B + SBC A,0 + LD B,A + LD A,0 + RET + .BYTE 0,0,0 ; Random number seed table used by RND + .BYTE 035H,04AH,0CAH,099H ;-2.65145E+07 + .BYTE 039H,01CH,076H,098H ; 1.61291E+07 + .BYTE 022H,095H,0B3H,098H ;-1.17691E+07 + .BYTE 00AH,0DDH,047H,098H ; 1.30983E+07 + .BYTE 053H,0D1H,099H,099H ;-2-01612E+07 + .BYTE 00AH,01AH,09FH,098H ;-1.04269E+07 + .BYTE 065H,0BCH,0CDH,098H ;-1.34831E+07 + .BYTE 0D6H,077H,03EH,098H ; 1.24825E+07 + .BYTE 052H,0C7H,04FH,080H ; Last random number + IN A,(0) ; INP (x) skeleton + RET + .BYTE 1 ; POS (x) number (1) + .BYTE 255 ; Terminal width (255 = no auto CRLF) + .BYTE 28 ; Width for commas (3 columns) + .BYTE 0 ; No nulls after input bytes + .BYTE 0 ; Output enabled (^O off) + .WORD 20 ; Initial lines counter + .WORD 20 ; Initial lines number + .WORD 0 ; Array load/save check sum + .BYTE 0 ; Break not by NMI + .BYTE 0 ; Break flag + JP TTYLIN ; Input reflection (set to TTY) + JP $0000 ; POINT reflection unused + JP $0000 ; SET reflection + JP $0000 ; RESET reflection + .WORD STLOOK ; Temp string space + .WORD -2 ; Current line number (cold) + .WORD PROGST+1 ; Start of program text +INITBE: + +; END OF INITIALISATION TABLE --------------------------------------------------- + +ERRMSG: .BYTE " Error",0 +INMSG: .BYTE " in ",0 +ZERBYT .EQU $-1 ; A zero byte +OKMSG: .BYTE "Ok",CR,LF,0,0 +BRKMSG: .BYTE "Break",0 + +BAKSTK: LD HL,4 ; Look for "FOR" block with + ADD HL,SP ; same index as specified +LOKFOR: LD A,(HL) ; Get block ID + INC HL ; Point to index address + CP ZFOR ; Is it a "FOR" token + RET NZ ; No - exit + LD C,(HL) ; BC = Address of "FOR" index + INC HL + LD B,(HL) + INC HL ; Point to sign of STEP + PUSH HL ; Save pointer to sign + LD L,C ; HL = address of "FOR" index + LD H,B + LD A,D ; See if an index was specified + OR E ; DE = 0 if no index specified + EX DE,HL ; Specified index into HL + JP Z,INDFND ; Skip if no index given + EX DE,HL ; Index back into DE + CALL CPDEHL ; Compare index with one given +INDFND: LD BC,16-3 ; Offset to next block + POP HL ; Restore pointer to sign + RET Z ; Return if block found + ADD HL,BC ; Point to next block + JP LOKFOR ; Keep on looking + +MOVUP: CALL ENFMEM ; See if enough memory +MOVSTR: PUSH BC ; Save end of source + EX (SP),HL ; Swap source and dest" end + POP BC ; Get end of destination +MOVLP: CALL CPDEHL ; See if list moved + LD A,(HL) ; Get byte + LD (BC),A ; Move it + RET Z ; Exit if all done + DEC BC ; Next byte to move to + DEC HL ; Next byte to move + JP MOVLP ; Loop until all bytes moved + +CHKSTK: PUSH HL ; Save code string address + LD HL,(ARREND) ; Lowest free memory + LD B,0 ; BC = Number of levels to test + ADD HL,BC ; 2 Bytes for each level + ADD HL,BC + .BYTE 3EH ; Skip "PUSH HL" +ENFMEM: PUSH HL ; Save code string address + LD A,0D0H ;LOW -48 ; 48 Bytes minimum RAM + SUB L + LD L,A + LD A,0FFH; HIGH (-48) ; 48 Bytes minimum RAM + SBC A,H + JP C,OMERR ; Not enough - ?OM Error + LD H,A + ADD HL,SP ; Test if stack is overflowed + POP HL ; Restore code string address + RET C ; Return if enough mmory +OMERR: LD E,OM ; ?OM Error + JP ERROR + +DATSNR: LD HL,(DATLIN) ; Get line of current DATA item + LD (LINEAT),HL ; Save as current line +SNERR: LD E,SN ; ?SN Error + .BYTE 01H ; Skip "LD E,DZ" +DZERR: LD E,DZ ; ?/0 Error + .BYTE 01H ; Skip "LD E,NF" +NFERR: LD E,NF ; ?NF Error + .BYTE 01H ; Skip "LD E,DD" +DDERR: LD E,DD ; ?DD Error + .BYTE 01H ; Skip "LD E,UF" +UFERR: LD E,UF ; ?UF Error + .BYTE 01H ; Skip "LD E,OV +OVERR: LD E,OV ; ?OV Error + .BYTE 01H ; Skip "LD E,TM" +TMERR: LD E,TM ; ?TM Error + +ERROR: CALL CLREG ; Clear registers and stack + LD (CTLOFG),A ; Enable output (A is 0) + CALL STTLIN ; Start new line + LD HL,ERRORS ; Point to error codes + LD D,A ; D = 0 (A is 0) + LD A,'?' + CALL OUTC ; Output '?' + ADD HL,DE ; Offset to correct error code + LD A,(HL) ; First character + CALL OUTC ; Output it + CALL GETCHR ; Get next character + CALL OUTC ; Output it + LD HL,ERRMSG ; "Error" message +ERRIN: CALL PRS ; Output message + LD HL,(LINEAT) ; Get line of error + LD DE,-2 ; Cold start error if -2 + CALL CPDEHL ; See if cold start error + JP Z,CSTART ; Cold start error - Restart + LD A,H ; Was it a direct error? + AND L ; Line = -1 if direct error + INC A + CALL NZ,LINEIN ; No - output line of error + .BYTE 3EH ; Skip "POP BC" +POPNOK: POP BC ; Drop address in input buffer + +PRNTOK: XOR A ; Output "Ok" and get command + LD (CTLOFG),A ; Enable output + CALL STTLIN ; Start new line + LD HL,OKMSG ; "Ok" message + CALL PRS ; Output "Ok" +GETCMD: LD HL,-1 ; Flag direct mode + LD (LINEAT),HL ; Save as current line + CALL GETLIN ; Get an input line + JP C,GETCMD ; Get line again if break + CALL GETCHR ; Get first character + INC A ; Test if end of line + DEC A ; Without affecting Carry + JP Z,GETCMD ; Nothing entered - Get another + PUSH AF ; Save Carry status + CALL ATOH ; Get line number into DE + PUSH DE ; Save line number + CALL CRUNCH ; Tokenise rest of line + LD B,A ; Length of tokenised line + POP DE ; Restore line number + POP AF ; Restore Carry + JP NC,EXCUTE ; No line number - Direct mode + PUSH DE ; Save line number + PUSH BC ; Save length of tokenised line + XOR A + LD (LSTBIN),A ; Clear last byte input + CALL GETCHR ; Get next character + OR A ; Set flags + PUSH AF ; And save them + CALL SRCHLN ; Search for line number in DE + JP C,LINFND ; Jump if line found + POP AF ; Get status + PUSH AF ; And re-save + JP Z,ULERR ; Nothing after number - Error + OR A ; Clear Carry +LINFND: PUSH BC ; Save address of line in prog + JP NC,INEWLN ; Line not found - Insert new + EX DE,HL ; Next line address in DE + LD HL,(PROGND) ; End of program +SFTPRG: LD A,(DE) ; Shift rest of program down + LD (BC),A + INC BC ; Next destination + INC DE ; Next source + CALL CPDEHL ; All done? + JP NZ,SFTPRG ; More to do + LD H,B ; HL - New end of program + LD L,C + LD (PROGND),HL ; Update end of program + +INEWLN: POP DE ; Get address of line, + POP AF ; Get status + JP Z,SETPTR ; No text - Set up pointers + LD HL,(PROGND) ; Get end of program + EX (SP),HL ; Get length of input line + POP BC ; End of program to BC + ADD HL,BC ; Find new end + PUSH HL ; Save new end + CALL MOVUP ; Make space for line + POP HL ; Restore new end + LD (PROGND),HL ; Update end of program pointer + EX DE,HL ; Get line to move up in HL + LD (HL),H ; Save MSB + POP DE ; Get new line number + INC HL ; Skip pointer + INC HL + LD (HL),E ; Save LSB of line number + INC HL + LD (HL),D ; Save MSB of line number + INC HL ; To first byte in line + LD DE,BUFFER ; Copy buffer to program +MOVBUF: LD A,(DE) ; Get source + LD (HL),A ; Save destinations + INC HL ; Next source + INC DE ; Next destination + OR A ; Done? + JP NZ,MOVBUF ; No - Repeat +SETPTR: CALL RUNFST ; Set line pointers + INC HL ; To LSB of pointer + EX DE,HL ; Address to DE +PTRLP: LD H,D ; Address to HL + LD L,E + LD A,(HL) ; Get LSB of pointer + INC HL ; To MSB of pointer + OR (HL) ; Compare with MSB pointer + JP Z,GETCMD ; Get command line if end + INC HL ; To LSB of line number + INC HL ; Skip line number + INC HL ; Point to first byte in line + XOR A ; Looking for 00 byte +FNDEND: CP (HL) ; Found end of line? + INC HL ; Move to next byte + JP NZ,FNDEND ; No - Keep looking + EX DE,HL ; Next line address to HL + LD (HL),E ; Save LSB of pointer + INC HL + LD (HL),D ; Save MSB of pointer + JP PTRLP ; Do next line + +SRCHLN: LD HL,(BASTXT) ; Start of program text +SRCHLP: LD B,H ; BC = Address to look at + LD C,L + LD A,(HL) ; Get address of next line + INC HL + OR (HL) ; End of program found? + DEC HL + RET Z ; Yes - Line not found + INC HL + INC HL + LD A,(HL) ; Get LSB of line number + INC HL + LD H,(HL) ; Get MSB of line number + LD L,A + CALL CPDEHL ; Compare with line in DE + LD H,B ; HL = Start of this line + LD L,C + LD A,(HL) ; Get LSB of next line address + INC HL + LD H,(HL) ; Get MSB of next line address + LD L,A ; Next line to HL + CCF + RET Z ; Lines found - Exit + CCF + RET NC ; Line not found,at line after + JP SRCHLP ; Keep looking + +NEW: RET NZ ; Return if any more on line +CLRPTR: LD HL,(BASTXT) ; Point to start of program + XOR A ; Set program area to empty + LD (HL),A ; Save LSB = 00 + INC HL + LD (HL),A ; Save MSB = 00 + INC HL + LD (PROGND),HL ; Set program end + +RUNFST: LD HL,(BASTXT) ; Clear all variables + DEC HL + +INTVAR: LD (BRKLIN),HL ; Initialise RUN variables + LD HL,(LSTRAM) ; Get end of RAM + LD (STRBOT),HL ; Clear string space + XOR A + CALL RESTOR ; Reset DATA pointers + LD HL,(PROGND) ; Get end of program + LD (VAREND),HL ; Clear variables + LD (ARREND),HL ; Clear arrays + +CLREG: POP BC ; Save return address + LD HL,(STRSPC) ; Get end of working RAN + LD SP,HL ; Set stack + LD HL,TMSTPL ; Temporary string pool + LD (TMSTPT),HL ; Reset temporary string ptr + XOR A ; A = 00 + LD L,A ; HL = 0000 + LD H,A + LD (CONTAD),HL ; No CONTinue + LD (FORFLG),A ; Clear FOR flag + LD (FNRGNM),HL ; Clear FN argument + PUSH HL ; HL = 0000 + PUSH BC ; Put back return +DOAGN: LD HL,(BRKLIN) ; Get address of code to RUN + RET ; Return to execution driver + +PROMPT: LD A,'?' ; '?' + CALL OUTC ; Output character + LD A,' ' ; Space + CALL OUTC ; Output character + JP RINPUT ; Get input line + +CRUNCH: XOR A ; Tokenise line @ HL to BUFFER + LD (DATFLG),A ; Reset literal flag + LD C,2+3 ; 2 byte number and 3 nulls + LD DE,BUFFER ; Start of input buffer +CRNCLP: LD A,(HL) ; Get byte + CP ' ' ; Is it a space? + JP Z,MOVDIR ; Yes - Copy direct + LD B,A ; Save character + CP '"' ; Is it a quote? + JP Z,CPYLIT ; Yes - Copy literal string + OR A ; Is it end of buffer? + JP Z,ENDBUF ; Yes - End buffer + LD A,(DATFLG) ; Get data type + OR A ; Literal? + LD A,(HL) ; Get byte to copy + JP NZ,MOVDIR ; Literal - Copy direct + CP '?' ; Is it '?' short for PRINT + LD A,ZPRINT ; "PRINT" token + JP Z,MOVDIR ; Yes - replace it + LD A,(HL) ; Get byte again + CP '0' ; Is it less than '0' + JP C,FNDWRD ; Yes - Look for reserved words + CP 60; ";"+1 ; Is it "0123456789:;" ? + JP C,MOVDIR ; Yes - copy it direct +FNDWRD: PUSH DE ; Look for reserved words + LD DE,WORDS-1 ; Point to table + PUSH BC ; Save count + LD BC,RETNAD ; Where to return to + PUSH BC ; Save return address + LD B,ZEND-1 ; First token value -1 + LD A,(HL) ; Get byte + CP 'a' ; Less than 'a' ? + JP C,SEARCH ; Yes - search for words + CP 'z'+1 ; Greater than 'z' ? + JP NC,SEARCH ; Yes - search for words + AND 01011111B ; Force upper case + LD (HL),A ; Replace byte +SEARCH: LD C,(HL) ; Search for a word + EX DE,HL +GETNXT: INC HL ; Get next reserved word + OR (HL) ; Start of word? + JP P,GETNXT ; No - move on + INC B ; Increment token value + LD A, (HL) ; Get byte from table + AND 01111111B ; Strip bit 7 + RET Z ; Return if end of list + CP C ; Same character as in buffer? + JP NZ,GETNXT ; No - get next word + EX DE,HL + PUSH HL ; Save start of word + +NXTBYT: INC DE ; Look through rest of word + LD A,(DE) ; Get byte from table + OR A ; End of word ? + JP M,MATCH ; Yes - Match found + LD C,A ; Save it + LD A,B ; Get token value + CP ZGOTO ; Is it "GOTO" token ? + JP NZ,NOSPC ; No - Don't allow spaces + CALL GETCHR ; Get next character + DEC HL ; Cancel increment from GETCHR +NOSPC: INC HL ; Next byte + LD A,(HL) ; Get byte + CP 'a' ; Less than 'a' ? + JP C,NOCHNG ; Yes - don't change + AND 01011111B ; Make upper case +NOCHNG: CP C ; Same as in buffer ? + JP Z,NXTBYT ; Yes - keep testing + POP HL ; Get back start of word + JP SEARCH ; Look at next word + +MATCH: LD C,B ; Word found - Save token value + POP AF ; Throw away return + EX DE,HL + RET ; Return to "RETNAD" +RETNAD: EX DE,HL ; Get address in string + LD A,C ; Get token value + POP BC ; Restore buffer length + POP DE ; Get destination address +MOVDIR: INC HL ; Next source in buffer + LD (DE),A ; Put byte in buffer + INC DE ; Move up buffer + INC C ; Increment length of buffer + SUB ':' ; End of statement? + JP Z,SETLIT ; Jump if multi-statement line + CP ZDATA-3AH ; Is it DATA statement ? + JP NZ,TSTREM ; No - see if REM +SETLIT: LD (DATFLG),A ; Set literal flag +TSTREM: SUB ZREM-3AH ; Is it REM? + JP NZ,CRNCLP ; No - Leave flag + LD B,A ; Copy rest of buffer +NXTCHR: LD A,(HL) ; Get byte + OR A ; End of line ? + JP Z,ENDBUF ; Yes - Terminate buffer + CP B ; End of statement ? + JP Z,MOVDIR ; Yes - Get next one +CPYLIT: INC HL ; Move up source string + LD (DE),A ; Save in destination + INC C ; Increment length + INC DE ; Move up destination + JP NXTCHR ; Repeat + +ENDBUF: LD HL,BUFFER-1 ; Point to start of buffer + LD (DE),A ; Mark end of buffer (A = 00) + INC DE + LD (DE),A ; A = 00 + INC DE + LD (DE),A ; A = 00 + RET + +DODEL: LD A,(NULFLG) ; Get null flag status + OR A ; Is it zero? + LD A,0 ; Zero A - Leave flags + LD (NULFLG),A ; Zero null flag + JP NZ,ECHDEL ; Set - Echo it + DEC B ; Decrement length + JP Z,GETLIN ; Get line again if empty + CALL OUTC ; Output null character + .BYTE 3EH ; Skip "DEC B" +ECHDEL: DEC B ; Count bytes in buffer + DEC HL ; Back space buffer + JP Z,OTKLN ; No buffer - Try again + LD A,(HL) ; Get deleted byte + CALL OUTC ; Echo it + JP MORINP ; Get more input + +DELCHR: DEC B ; Count bytes in buffer + DEC HL ; Back space buffer + CALL OUTC ; Output character in A + JP NZ,MORINP ; Not end - Get more +OTKLN: CALL OUTC ; Output character in A +KILIN: CALL PRNTCRLF ; Output CRLF + JP TTYLIN ; Get line again + +GETLIN: +TTYLIN: LD HL,BUFFER ; Get a line by character + LD B,1 ; Set buffer as empty + XOR A + LD (NULFLG),A ; Clear null flag +MORINP: CALL CLOTST ; Get character and test ^O + LD C,A ; Save character in C + CP DEL ; Delete character? + JP Z,DODEL ; Yes - Process it + LD A,(NULFLG) ; Get null flag + OR A ; Test null flag status + JP Z,PROCES ; Reset - Process character + LD A,0 ; Set a null + CALL OUTC ; Output null + XOR A ; Clear A + LD (NULFLG),A ; Reset null flag +PROCES: LD A,C ; Get character + CP CTRLG ; Bell? + JP Z,PUTCTL ; Yes - Save it + CP CTRLC ; Is it control "C"? + CALL Z,PRNTCRLF ; Yes - Output CRLF + SCF ; Flag break + RET Z ; Return if control "C" + CP CR ; Is it enter? + JP Z,ENDINP ; Yes - Terminate input + CP CTRLU ; Is it control "U"? + JP Z,KILIN ; Yes - Get another line + CP '@' ; Is it "kill line"? + JP Z,OTKLN ; Yes - Kill line + CP '_' ; Is it delete? + JP Z,DELCHR ; Yes - Delete character + CP BKSP ; Is it backspace? + JP Z,DELCHR ; Yes - Delete character + CP CTRLR ; Is it control "R"? + JP NZ,PUTBUF ; No - Put in buffer + PUSH BC ; Save buffer length + PUSH DE ; Save DE + PUSH HL ; Save buffer address + LD (HL),0 ; Mark end of buffer + CALL OUTNCR ; Output and do CRLF + LD HL,BUFFER ; Point to buffer start + CALL PRS ; Output buffer + POP HL ; Restore buffer address + POP DE ; Restore DE + POP BC ; Restore buffer length + JP MORINP ; Get another character + +PUTBUF: CP ' ' ; Is it a control code? + JP C,MORINP ; Yes - Ignore +PUTCTL: LD A,B ; Get number of bytes in buffer + CP 72+1 ; Test for line overflow + LD A,CTRLG ; Set a bell + JP NC,OUTNBS ; Ring bell if buffer full + LD A,C ; Get character + LD (HL),C ; Save in buffer + LD (LSTBIN),A ; Save last input byte + INC HL ; Move up buffer + INC B ; Increment length +OUTIT: CALL OUTC ; Output the character entered + JP MORINP ; Get another character + +OUTNBS: CALL OUTC ; Output bell and back over it + LD A,BKSP ; Set back space + JP OUTIT ; Output it and get more + +CPDEHL: LD A,H ; Get H + SUB D ; Compare with D + RET NZ ; Different - Exit + LD A,L ; Get L + SUB E ; Compare with E + RET ; Return status + +CHKSYN: LD A,(HL) ; Check syntax of character + EX (SP),HL ; Address of test byte + CP (HL) ; Same as in code string? + INC HL ; Return address + EX (SP),HL ; Put it back + JP Z,GETCHR ; Yes - Get next character + JP SNERR ; Different - ?SN Error + +OUTC: PUSH AF ; Save character + LD A,(CTLOFG) ; Get control "O" flag + OR A ; Is it set? + JP NZ,POPAF ; Yes - don't output + POP AF ; Restore character + PUSH BC ; Save buffer length + PUSH AF ; Save character + CP ' ' ; Is it a control code? + JP C,DINPOS ; Yes - Don't INC POS(X) + LD A,(LWIDTH) ; Get line width + LD B,A ; To B + LD A,(CURPOS) ; Get cursor position + INC B ; Width 255? + JP Z,INCLEN ; Yes - No width limit + DEC B ; Restore width + CP B ; At end of line? + CALL Z,PRNTCRLF ; Yes - output CRLF +INCLEN: INC A ; Move on one character + LD (CURPOS),A ; Save new position +DINPOS: POP AF ; Restore character + POP BC ; Restore buffer length + CALL MONOUT ; Send it + RET + +CLOTST: CALL GETINP ; Get input character + AND 01111111B ; Strip bit 7 + CP CTRLO ; Is it control "O"? + RET NZ ; No don't flip flag + LD A,(CTLOFG) ; Get flag + CPL ; Flip it + LD (CTLOFG),A ; Put it back + XOR A ; Null character + RET + +LIST: CALL ATOH ; ASCII number to DE + RET NZ ; Return if anything extra + POP BC ; Rubbish - Not needed + CALL SRCHLN ; Search for line number in DE + PUSH BC ; Save address of line + CALL SETLIN ; Set up lines counter +LISTLP: POP HL ; Restore address of line + LD C,(HL) ; Get LSB of next line + INC HL + LD B,(HL) ; Get MSB of next line + INC HL + LD A,B ; BC = 0 (End of program)? + OR C + JP Z,PRNTOK ; Yes - Go to command mode + CALL COUNT ; Count lines + CALL TSTBRK ; Test for break key + PUSH BC ; Save address of next line + CALL PRNTCRLF ; Output CRLF + LD E,(HL) ; Get LSB of line number + INC HL + LD D,(HL) ; Get MSB of line number + INC HL + PUSH HL ; Save address of line start + EX DE,HL ; Line number to HL + CALL PRNTHL ; Output line number in decimal + LD A,' ' ; Space after line number + POP HL ; Restore start of line address +LSTLP2: CALL OUTC ; Output character in A +LSTLP3: LD A,(HL) ; Get next byte in line + OR A ; End of line? + INC HL ; To next byte in line + JP Z,LISTLP ; Yes - get next line + JP P,LSTLP2 ; No token - output it + SUB ZEND-1 ; Find and output word + LD C,A ; Token offset+1 to C + LD DE,WORDS ; Reserved word list +FNDTOK: LD A,(DE) ; Get character in list + INC DE ; Move on to next + OR A ; Is it start of word? + JP P,FNDTOK ; No - Keep looking for word + DEC C ; Count words + JP NZ,FNDTOK ; Not there - keep looking +OUTWRD: AND 01111111B ; Strip bit 7 + CALL OUTC ; Output first character + LD A,(DE) ; Get next character + INC DE ; Move on to next + OR A ; Is it end of word? + JP P,OUTWRD ; No - output the rest + JP LSTLP3 ; Next byte in line + +SETLIN: PUSH HL ; Set up LINES counter + LD HL,(LINESN) ; Get LINES number + LD (LINESC),HL ; Save in LINES counter + POP HL + RET + +COUNT: PUSH HL ; Save code string address + PUSH DE + LD HL,(LINESC) ; Get LINES counter + LD DE,-1 + ADC HL,DE ; Decrement + LD (LINESC),HL ; Put it back + POP DE + POP HL ; Restore code string address + RET P ; Return if more lines to go + PUSH HL ; Save code string address + LD HL,(LINESN) ; Get LINES number + LD (LINESC),HL ; Reset LINES counter + CALL GETINP ; Get input character + CP CTRLC ; Is it control "C"? + JP Z,RSLNBK ; Yes - Reset LINES and break + POP HL ; Restore code string address + JP COUNT ; Keep on counting + +RSLNBK: LD HL,(LINESN) ; Get LINES number + LD (LINESC),HL ; Reset LINES counter + JP BRKRET ; Go and output "Break" + +FOR: LD A,64H ; Flag "FOR" assignment + LD (FORFLG),A ; Save "FOR" flag + CALL LET ; Set up initial index + POP BC ; Drop RETurn address + PUSH HL ; Save code string address + CALL DATA ; Get next statement address + LD (LOOPST),HL ; Save it for start of loop + LD HL,2 ; Offset for "FOR" block + ADD HL,SP ; Point to it +FORSLP: CALL LOKFOR ; Look for existing "FOR" block + POP DE ; Get code string address + JP NZ,FORFND ; No nesting found + ADD HL,BC ; Move into "FOR" block + PUSH DE ; Save code string address + DEC HL + LD D,(HL) ; Get MSB of loop statement + DEC HL + LD E,(HL) ; Get LSB of loop statement + INC HL + INC HL + PUSH HL ; Save block address + LD HL,(LOOPST) ; Get address of loop statement + CALL CPDEHL ; Compare the FOR loops + POP HL ; Restore block address + JP NZ,FORSLP ; Different FORs - Find another + POP DE ; Restore code string address + LD SP,HL ; Remove all nested loops + +FORFND: EX DE,HL ; Code string address to HL + LD C,8 + CALL CHKSTK ; Check for 8 levels of stack + PUSH HL ; Save code string address + LD HL,(LOOPST) ; Get first statement of loop + EX (SP),HL ; Save and restore code string + PUSH HL ; Re-save code string address + LD HL,(LINEAT) ; Get current line number + EX (SP),HL ; Save and restore code string + CALL TSTNUM ; Make sure it's a number + CALL CHKSYN ; Make sure "TO" is next + .BYTE ZTO ; "TO" token + CALL GETNUM ; Get "TO" expression value + PUSH HL ; Save code string address + CALL BCDEFP ; Move "TO" value to BCDE + POP HL ; Restore code string address + PUSH BC ; Save "TO" value in block + PUSH DE + LD BC,8100H ; BCDE - 1 (default STEP) + LD D,C ; C=0 + LD E,D ; D=0 + LD A,(HL) ; Get next byte in code string + CP ZSTEP ; See if "STEP" is stated + LD A,1 ; Sign of step = 1 + JP NZ,SAVSTP ; No STEP given - Default to 1 + CALL GETCHR ; Jump over "STEP" token + CALL GETNUM ; Get step value + PUSH HL ; Save code string address + CALL BCDEFP ; Move STEP to BCDE + CALL TSTSGN ; Test sign of FPREG + POP HL ; Restore code string address +SAVSTP: PUSH BC ; Save the STEP value in block + PUSH DE + PUSH AF ; Save sign of STEP + INC SP ; Don't save flags + PUSH HL ; Save code string address + LD HL,(BRKLIN) ; Get address of index variable + EX (SP),HL ; Save and restore code string +PUTFID: LD B,ZFOR ; "FOR" block marker + PUSH BC ; Save it + INC SP ; Don't save C + +RUNCNT: CALL TSTBRK ; Execution driver - Test break + LD (BRKLIN),HL ; Save code address for break + LD A,(HL) ; Get next byte in code string + CP ':' ; Multi statement line? + JP Z,EXCUTE ; Yes - Execute it + OR A ; End of line? + JP NZ,SNERR ; No - Syntax error + INC HL ; Point to address of next line + LD A,(HL) ; Get LSB of line pointer + INC HL + OR (HL) ; Is it zero (End of prog)? + JP Z,ENDPRG ; Yes - Terminate execution + INC HL ; Point to line number + LD E,(HL) ; Get LSB of line number + INC HL + LD D,(HL) ; Get MSB of line number + EX DE,HL ; Line number to HL + LD (LINEAT),HL ; Save as current line number + EX DE,HL ; Line number back to DE +EXCUTE: CALL GETCHR ; Get key word + LD DE,RUNCNT ; Where to RETurn to + PUSH DE ; Save for RETurn +IFJMP: RET Z ; Go to RUNCNT if end of STMT +ONJMP: SUB ZEND ; Is it a token? + JP C,LET ; No - try to assign it + CP ZNEW+1-ZEND ; END to NEW ? + JP NC,SNERR ; Not a key word - ?SN Error + RLCA ; Double it + LD C,A ; BC = Offset into table + LD B,0 + EX DE,HL ; Save code string address + LD HL,WORDTB ; Keyword address table + ADD HL,BC ; Point to routine address + LD C,(HL) ; Get LSB of routine address + INC HL + LD B,(HL) ; Get MSB of routine address + PUSH BC ; Save routine address + EX DE,HL ; Restore code string address + +GETCHR: INC HL ; Point to next character + LD A,(HL) ; Get next code string byte + CP ':' ; Z if ':' + RET NC ; NC if > "9" + CP ' ' + JP Z,GETCHR ; Skip over spaces + CP '0' + CCF ; NC if < '0' + INC A ; Test for zero - Leave carry + DEC A ; Z if Null + RET + +RESTOR: EX DE,HL ; Save code string address + LD HL,(BASTXT) ; Point to start of program + JP Z,RESTNL ; Just RESTORE - reset pointer + EX DE,HL ; Restore code string address + CALL ATOH ; Get line number to DE + PUSH HL ; Save code string address + CALL SRCHLN ; Search for line number in DE + LD H,B ; HL = Address of line + LD L,C + POP DE ; Restore code string address + JP NC,ULERR ; ?UL Error if not found +RESTNL: DEC HL ; Byte before DATA statement +UPDATA: LD (NXTDAT),HL ; Update DATA pointer + EX DE,HL ; Restore code string address + RET + + +TSTBRK: RST 18H ; Check input status + RET Z ; No key, go back + RST 10H ; Get the key into A + CP ESC ; Escape key? + JR Z,BRK ; Yes, break + CP CTRLC ; + JR Z,BRK ; Yes, break + CP CTRLS ; Stop scrolling? + RET NZ ; Other key, ignore + + +STALL: RST 10H ; Wait for key + CP CTRLQ ; Resume scrolling? + RET Z ; Release the chokehold + CP CTRLC ; Second break? + JR Z,STOP ; Break during hold exits prog + JR STALL ; Loop until or + +BRK LD A,$FF ; Set BRKFLG + LD (BRKFLG),A ; Store it + + +STOP: RET NZ ; Exit if anything else + .BYTE 0F6H ; Flag "STOP" +PEND: RET NZ ; Exit if anything else + LD (BRKLIN),HL ; Save point of break + .BYTE 21H ; Skip "OR 11111111B" +INPBRK: OR 11111111B ; Flag "Break" wanted + POP BC ; Return not needed and more +ENDPRG: LD HL,(LINEAT) ; Get current line number + PUSH AF ; Save STOP / END status + LD A,L ; Is it direct break? + AND H + INC A ; Line is -1 if direct break + JP Z,NOLIN ; Yes - No line number + LD (ERRLIN),HL ; Save line of break + LD HL,(BRKLIN) ; Get point of break + LD (CONTAD),HL ; Save point to CONTinue +NOLIN: XOR A + LD (CTLOFG),A ; Enable output + CALL STTLIN ; Start a new line + POP AF ; Restore STOP / END status + LD HL,BRKMSG ; "Break" message + JP NZ,ERRIN ; "in line" wanted? + JP PRNTOK ; Go to command mode + +CONT: LD HL,(CONTAD) ; Get CONTinue address + LD A,H ; Is it zero? + OR L + LD E,CN ; ?CN Error + JP Z,ERROR ; Yes - output "?CN Error" + EX DE,HL ; Save code string address + LD HL,(ERRLIN) ; Get line of last break + LD (LINEAT),HL ; Set up current line number + EX DE,HL ; Restore code string address + RET ; CONTinue where left off + +NULL: CALL GETINT ; Get integer 0-255 + RET NZ ; Return if bad value + LD (NULLS),A ; Set nulls number + RET + + +ACCSUM: PUSH HL ; Save address in array + LD HL,(CHKSUM) ; Get check sum + LD B,0 ; BC - Value of byte + LD C,A + ADD HL,BC ; Add byte to check sum + LD (CHKSUM),HL ; Re-save check sum + POP HL ; Restore address in array + RET + +CHKLTR: LD A,(HL) ; Get byte + CP 'A' ; < 'a' ? + RET C ; Carry set if not letter + CP 'Z'+1 ; > 'z' ? + CCF + RET ; Carry set if not letter + +FPSINT: CALL GETCHR ; Get next character +POSINT: CALL GETNUM ; Get integer 0 to 32767 +DEPINT: CALL TSTSGN ; Test sign of FPREG + JP M,FCERR ; Negative - ?FC Error +DEINT: LD A,(FPEXP) ; Get integer value to DE + CP 80H+16 ; Exponent in range (16 bits)? + JP C,FPINT ; Yes - convert it + LD BC,9080H ; BCDE = -32768 + LD DE,0000 + PUSH HL ; Save code string address + CALL CMPNUM ; Compare FPREG with BCDE + POP HL ; Restore code string address + LD D,C ; MSB to D + RET Z ; Return if in range +FCERR: LD E,FC ; ?FC Error + JP ERROR ; Output error- + +ATOH: DEC HL ; ASCII number to DE binary +GETLN: LD DE,0 ; Get number to DE +GTLNLP: CALL GETCHR ; Get next character + RET NC ; Exit if not a digit + PUSH HL ; Save code string address + PUSH AF ; Save digit + LD HL,65529/10 ; Largest number 65529 + CALL CPDEHL ; Number in range? + JP C,SNERR ; No - ?SN Error + LD H,D ; HL = Number + LD L,E + ADD HL,DE ; Times 2 + ADD HL,HL ; Times 4 + ADD HL,DE ; Times 5 + ADD HL,HL ; Times 10 + POP AF ; Restore digit + SUB '0' ; Make it 0 to 9 + LD E,A ; DE = Value of digit + LD D,0 + ADD HL,DE ; Add to number + EX DE,HL ; Number to DE + POP HL ; Restore code string address + JP GTLNLP ; Go to next character + +CLEAR: JP Z,INTVAR ; Just "CLEAR" Keep parameters + CALL POSINT ; Get integer 0 to 32767 to DE + DEC HL ; Cancel increment + CALL GETCHR ; Get next character + PUSH HL ; Save code string address + LD HL,(LSTRAM) ; Get end of RAM + JP Z,STORED ; No value given - Use stored + POP HL ; Restore code string address + CALL CHKSYN ; Check for comma + .BYTE ',' + PUSH DE ; Save number + CALL POSINT ; Get integer 0 to 32767 + DEC HL ; Cancel increment + CALL GETCHR ; Get next character + JP NZ,SNERR ; ?SN Error if more on line + EX (SP),HL ; Save code string address + EX DE,HL ; Number to DE +STORED: LD A,L ; Get LSB of new RAM top + SUB E ; Subtract LSB of string space + LD E,A ; Save LSB + LD A,H ; Get MSB of new RAM top + SBC A,D ; Subtract MSB of string space + LD D,A ; Save MSB + JP C,OMERR ; ?OM Error if not enough mem + PUSH HL ; Save RAM top + LD HL,(PROGND) ; Get program end + LD BC,40 ; 40 Bytes minimum working RAM + ADD HL,BC ; Get lowest address + CALL CPDEHL ; Enough memory? + JP NC,OMERR ; No - ?OM Error + EX DE,HL ; RAM top to HL + LD (STRSPC),HL ; Set new string space + POP HL ; End of memory to use + LD (LSTRAM),HL ; Set new top of RAM + POP HL ; Restore code string address + JP INTVAR ; Initialise variables + +RUN: JP Z,RUNFST ; RUN from start if just RUN + CALL INTVAR ; Initialise variables + LD BC,RUNCNT ; Execution driver loop + JP RUNLIN ; RUN from line number + +GOSUB: LD C,3 ; 3 Levels of stack needed + CALL CHKSTK ; Check for 3 levels of stack + POP BC ; Get return address + PUSH HL ; Save code string for RETURN + PUSH HL ; And for GOSUB routine + LD HL,(LINEAT) ; Get current line + EX (SP),HL ; Into stack - Code string out + LD A,ZGOSUB ; "GOSUB" token + PUSH AF ; Save token + INC SP ; Don't save flags + +RUNLIN: PUSH BC ; Save return address +GOTO: CALL ATOH ; ASCII number to DE binary + CALL REM ; Get end of line + PUSH HL ; Save end of line + LD HL,(LINEAT) ; Get current line + CALL CPDEHL ; Line after current? + POP HL ; Restore end of line + INC HL ; Start of next line + CALL C,SRCHLP ; Line is after current line + CALL NC,SRCHLN ; Line is before current line + LD H,B ; Set up code string address + LD L,C + DEC HL ; Incremented after + RET C ; Line found +ULERR: LD E,UL ; ?UL Error + JP ERROR ; Output error message + +RETURN: RET NZ ; Return if not just RETURN + LD D,-1 ; Flag "GOSUB" search + CALL BAKSTK ; Look "GOSUB" block + LD SP,HL ; Kill all FORs in subroutine + CP ZGOSUB ; Test for "GOSUB" token + LD E,RG ; ?RG Error + JP NZ,ERROR ; Error if no "GOSUB" found + POP HL ; Get RETURN line number + LD (LINEAT),HL ; Save as current + INC HL ; Was it from direct statement? + LD A,H + OR L ; Return to line + JP NZ,RETLIN ; No - Return to line + LD A,(LSTBIN) ; Any INPUT in subroutine? + OR A ; If so buffer is corrupted + JP NZ,POPNOK ; Yes - Go to command mode +RETLIN: LD HL,RUNCNT ; Execution driver loop + EX (SP),HL ; Into stack - Code string out + .BYTE 3EH ; Skip "POP HL" +NXTDTA: POP HL ; Restore code string address + +DATA: .BYTE 01H,3AH ; ':' End of statement +REM: LD C,0 ; 00 End of statement + LD B,0 +NXTSTL: LD A,C ; Statement and byte + LD C,B + LD B,A ; Statement end byte +NXTSTT: LD A,(HL) ; Get byte + OR A ; End of line? + RET Z ; Yes - Exit + CP B ; End of statement? + RET Z ; Yes - Exit + INC HL ; Next byte + CP '"' ; Literal string? + JP Z,NXTSTL ; Yes - Look for another '"' + JP NXTSTT ; Keep looking + +LET: CALL GETVAR ; Get variable name + CALL CHKSYN ; Make sure "=" follows + .BYTE ZEQUAL ; "=" token + PUSH DE ; Save address of variable + LD A,(TYPE) ; Get data type + PUSH AF ; Save type + CALL EVAL ; Evaluate expression + POP AF ; Restore type + EX (SP),HL ; Save code - Get var addr + LD (BRKLIN),HL ; Save address of variable + RRA ; Adjust type + CALL CHKTYP ; Check types are the same + JP Z,LETNUM ; Numeric - Move value +LETSTR: PUSH HL ; Save address of string var + LD HL,(FPREG) ; Pointer to string entry + PUSH HL ; Save it on stack + INC HL ; Skip over length + INC HL + LD E,(HL) ; LSB of string address + INC HL + LD D,(HL) ; MSB of string address + LD HL,(BASTXT) ; Point to start of program + CALL CPDEHL ; Is string before program? + JP NC,CRESTR ; Yes - Create string entry + LD HL,(STRSPC) ; Point to string space + CALL CPDEHL ; Is string literal in program? + POP DE ; Restore address of string + JP NC,MVSTPT ; Yes - Set up pointer + LD HL,TMPSTR ; Temporary string pool + CALL CPDEHL ; Is string in temporary pool? + JP NC,MVSTPT ; No - Set up pointer + .BYTE 3EH ; Skip "POP DE" +CRESTR: POP DE ; Restore address of string + CALL BAKTMP ; Back to last tmp-str entry + EX DE,HL ; Address of string entry + CALL SAVSTR ; Save string in string area +MVSTPT: CALL BAKTMP ; Back to last tmp-str entry + POP HL ; Get string pointer + CALL DETHL4 ; Move string pointer to var + POP HL ; Restore code string address + RET + +LETNUM: PUSH HL ; Save address of variable + CALL FPTHL ; Move value to variable + POP DE ; Restore address of variable + POP HL ; Restore code string address + RET + +ON: CALL GETINT ; Get integer 0-255 + LD A,(HL) ; Get "GOTO" or "GOSUB" token + LD B,A ; Save in B + CP ZGOSUB ; "GOSUB" token? + JP Z,ONGO ; Yes - Find line number + CALL CHKSYN ; Make sure it's "GOTO" + .BYTE ZGOTO ; "GOTO" token + DEC HL ; Cancel increment +ONGO: LD C,E ; Integer of branch value +ONGOLP: DEC C ; Count branches + LD A,B ; Get "GOTO" or "GOSUB" token + JP Z,ONJMP ; Go to that line if right one + CALL GETLN ; Get line number to DE + CP ',' ; Another line number? + RET NZ ; No - Drop through + JP ONGOLP ; Yes - loop + +IF: CALL EVAL ; Evaluate expression + LD A,(HL) ; Get token + CP ZGOTO ; "GOTO" token? + JP Z,IFGO ; Yes - Get line + CALL CHKSYN ; Make sure it's "THEN" + .BYTE ZTHEN ; "THEN" token + DEC HL ; Cancel increment +IFGO: CALL TSTNUM ; Make sure it's numeric + CALL TSTSGN ; Test state of expression + JP Z,REM ; False - Drop through + CALL GETCHR ; Get next character + JP C,GOTO ; Number - GOTO that line + JP IFJMP ; Otherwise do statement + +MRPRNT: DEC HL ; DEC 'cos GETCHR INCs + CALL GETCHR ; Get next character +PRINT: JP Z,PRNTCRLF ; CRLF if just PRINT +PRNTLP: RET Z ; End of list - Exit + CP ZTAB ; "TAB(" token? + JP Z,DOTAB ; Yes - Do TAB routine + CP ZSPC ; "SPC(" token? + JP Z,DOTAB ; Yes - Do SPC routine + PUSH HL ; Save code string address + CP ',' ; Comma? + JP Z,DOCOM ; Yes - Move to next zone + CP 59 ;";" ; Semi-colon? + JP Z,NEXITM ; Do semi-colon routine + POP BC ; Code string address to BC + CALL EVAL ; Evaluate expression + PUSH HL ; Save code string address + LD A,(TYPE) ; Get variable type + OR A ; Is it a string variable? + JP NZ,PRNTST ; Yes - Output string contents + CALL NUMASC ; Convert number to text + CALL CRTST ; Create temporary string + LD (HL),' ' ; Followed by a space + LD HL,(FPREG) ; Get length of output + INC (HL) ; Plus 1 for the space + LD HL,(FPREG) ; < Not needed > + LD A,(LWIDTH) ; Get width of line + LD B,A ; To B + INC B ; Width 255 (No limit)? + JP Z,PRNTNB ; Yes - Output number string + INC B ; Adjust it + LD A,(CURPOS) ; Get cursor position + ADD A,(HL) ; Add length of string + DEC A ; Adjust it + CP B ; Will output fit on this line? + CALL NC,PRNTCRLF ; No - CRLF first +PRNTNB: CALL PRS1 ; Output string at (HL) + XOR A ; Skip CALL by setting 'z' flag +PRNTST: CALL NZ,PRS1 ; Output string at (HL) + POP HL ; Restore code string address + JP MRPRNT ; See if more to PRINT + +STTLIN: LD A,(CURPOS) ; Make sure on new line + OR A ; Already at start? + RET Z ; Yes - Do nothing + JP PRNTCRLF ; Start a new line + +ENDINP: LD (HL),0 ; Mark end of buffer + LD HL,BUFFER-1 ; Point to buffer +PRNTCRLF: LD A,CR ; Load a CR + CALL OUTC ; Output character + LD A,LF ; Load a LF + CALL OUTC ; Output character +DONULL: XOR A ; Set to position 0 + LD (CURPOS),A ; Store it + LD A,(NULLS) ; Get number of nulls +NULLP: DEC A ; Count them + RET Z ; Return if done + PUSH AF ; Save count + XOR A ; Load a null + CALL OUTC ; Output it + POP AF ; Restore count + JP NULLP ; Keep counting + +DOCOM: LD A,(COMMAN) ; Get comma width + LD B,A ; Save in B + LD A,(CURPOS) ; Get current position + CP B ; Within the limit? + CALL NC,PRNTCRLF ; No - output CRLF + JP NC,NEXITM ; Get next item +ZONELP: SUB 14 ; Next zone of 14 characters + JP NC,ZONELP ; Repeat if more zones + CPL ; Number of spaces to output + JP ASPCS ; Output them + +DOTAB: PUSH AF ; Save token + CALL FNDNUM ; Evaluate expression + CALL CHKSYN ; Make sure ")" follows + .BYTE ")" + DEC HL ; Back space on to ")" + POP AF ; Restore token + SUB ZSPC ; Was it "SPC(" ? + PUSH HL ; Save code string address + JP Z,DOSPC ; Yes - Do 'E' spaces + LD A,(CURPOS) ; Get current position +DOSPC: CPL ; Number of spaces to print to + ADD A,E ; Total number to print + JP NC,NEXITM ; TAB < Current POS(X) +ASPCS: INC A ; Output A spaces + LD B,A ; Save number to print + LD A,' ' ; Space +SPCLP: CALL OUTC ; Output character in A + DEC B ; Count them + JP NZ,SPCLP ; Repeat if more +NEXITM: POP HL ; Restore code string address + CALL GETCHR ; Get next character + JP PRNTLP ; More to print + +REDO: .BYTE "?Redo from start",CR,LF,0 + +BADINP: LD A,(READFG) ; READ or INPUT? + OR A + JP NZ,DATSNR ; READ - ?SN Error + POP BC ; Throw away code string addr + LD HL,REDO ; "Redo from start" message + CALL PRS ; Output string + JP DOAGN ; Do last INPUT again + +INPUT: CALL IDTEST ; Test for illegal direct + LD A,(HL) ; Get character after "INPUT" + CP '"' ; Is there a prompt string? + LD A,0 ; Clear A and leave flags + LD (CTLOFG),A ; Enable output + JP NZ,NOPMPT ; No prompt - get input + CALL QTSTR ; Get string terminated by '"' + CALL CHKSYN ; Check for ';' after prompt + .BYTE ';' + PUSH HL ; Save code string address + CALL PRS1 ; Output prompt string + .BYTE 3EH ; Skip "PUSH HL" +NOPMPT: PUSH HL ; Save code string address + CALL PROMPT ; Get input with "? " prompt + POP BC ; Restore code string address + JP C,INPBRK ; Break pressed - Exit + INC HL ; Next byte + LD A,(HL) ; Get it + OR A ; End of line? + DEC HL ; Back again + PUSH BC ; Re-save code string address + JP Z,NXTDTA ; Yes - Find next DATA stmt + LD (HL),',' ; Store comma as separator + JP NXTITM ; Get next item + +READ: PUSH HL ; Save code string address + LD HL,(NXTDAT) ; Next DATA statement + .BYTE 0F6H ; Flag "READ" +NXTITM: XOR A ; Flag "INPUT" + LD (READFG),A ; Save "READ"/"INPUT" flag + EX (SP),HL ; Get code str' , Save pointer + JP GTVLUS ; Get values + +NEDMOR: CALL CHKSYN ; Check for comma between items + .BYTE ',' +GTVLUS: CALL GETVAR ; Get variable name + EX (SP),HL ; Save code str" , Get pointer + PUSH DE ; Save variable address + LD A,(HL) ; Get next "INPUT"/"DATA" byte + CP ',' ; Comma? + JP Z,ANTVLU ; Yes - Get another value + LD A,(READFG) ; Is it READ? + OR A + JP NZ,FDTLP ; Yes - Find next DATA stmt + LD A,'?' ; More INPUT needed + CALL OUTC ; Output character + CALL PROMPT ; Get INPUT with prompt + POP DE ; Variable address + POP BC ; Code string address + JP C,INPBRK ; Break pressed + INC HL ; Point to next DATA byte + LD A,(HL) ; Get byte + OR A ; Is it zero (No input) ? + DEC HL ; Back space INPUT pointer + PUSH BC ; Save code string address + JP Z,NXTDTA ; Find end of buffer + PUSH DE ; Save variable address +ANTVLU: LD A,(TYPE) ; Check data type + OR A ; Is it numeric? + JP Z,INPBIN ; Yes - Convert to binary + CALL GETCHR ; Get next character + LD D,A ; Save input character + LD B,A ; Again + CP '"' ; Start of literal sting? + JP Z,STRENT ; Yes - Create string entry + LD A,(READFG) ; "READ" or "INPUT" ? + OR A + LD D,A ; Save 00 if "INPUT" + JP Z,ITMSEP ; "INPUT" - End with 00 + LD D,':' ; "DATA" - End with 00 or ':' +ITMSEP: LD B,',' ; Item separator + DEC HL ; Back space for DTSTR +STRENT: CALL DTSTR ; Get string terminated by D + EX DE,HL ; String address to DE + LD HL,LTSTND ; Where to go after LETSTR + EX (SP),HL ; Save HL , get input pointer + PUSH DE ; Save address of string + JP LETSTR ; Assign string to variable + +INPBIN: CALL GETCHR ; Get next character + CALL ASCTFP ; Convert ASCII to FP number + EX (SP),HL ; Save input ptr, Get var addr + CALL FPTHL ; Move FPREG to variable + POP HL ; Restore input pointer +LTSTND: DEC HL ; DEC 'cos GETCHR INCs + CALL GETCHR ; Get next character + JP Z,MORDT ; End of line - More needed? + CP ',' ; Another value? + JP NZ,BADINP ; No - Bad input +MORDT: EX (SP),HL ; Get code string address + DEC HL ; DEC 'cos GETCHR INCs + CALL GETCHR ; Get next character + JP NZ,NEDMOR ; More needed - Get it + POP DE ; Restore DATA pointer + LD A,(READFG) ; "READ" or "INPUT" ? + OR A + EX DE,HL ; DATA pointer to HL + JP NZ,UPDATA ; Update DATA pointer if "READ" + PUSH DE ; Save code string address + OR (HL) ; More input given? + LD HL,EXTIG ; "?Extra ignored" message + CALL NZ,PRS ; Output string if extra given + POP HL ; Restore code string address + RET + +EXTIG: .BYTE "?Extra ignored",CR,LF,0 + +FDTLP: CALL DATA ; Get next statement + OR A ; End of line? + JP NZ,FANDT ; No - See if DATA statement + INC HL + LD A,(HL) ; End of program? + INC HL + OR (HL) ; 00 00 Ends program + LD E,OD ; ?OD Error + JP Z,ERROR ; Yes - Out of DATA + INC HL + LD E,(HL) ; LSB of line number + INC HL + LD D,(HL) ; MSB of line number + EX DE,HL + LD (DATLIN),HL ; Set line of current DATA item + EX DE,HL +FANDT: CALL GETCHR ; Get next character + CP ZDATA ; "DATA" token + JP NZ,FDTLP ; No "DATA" - Keep looking + JP ANTVLU ; Found - Convert input + +NEXT: LD DE,0 ; In case no index given +NEXT1: CALL NZ,GETVAR ; Get index address + LD (BRKLIN),HL ; Save code string address + CALL BAKSTK ; Look for "FOR" block + JP NZ,NFERR ; No "FOR" - ?NF Error + LD SP,HL ; Clear nested loops + PUSH DE ; Save index address + LD A,(HL) ; Get sign of STEP + INC HL + PUSH AF ; Save sign of STEP + PUSH DE ; Save index address + CALL PHLTFP ; Move index value to FPREG + EX (SP),HL ; Save address of TO value + PUSH HL ; Save address of index + CALL ADDPHL ; Add STEP to index value + POP HL ; Restore address of index + CALL FPTHL ; Move value to index variable + POP HL ; Restore address of TO value + CALL LOADFP ; Move TO value to BCDE + PUSH HL ; Save address of line of FOR + CALL CMPNUM ; Compare index with TO value + POP HL ; Restore address of line num + POP BC ; Address of sign of STEP + SUB B ; Compare with expected sign + CALL LOADFP ; BC = Loop stmt,DE = Line num + JP Z,KILFOR ; Loop finished - Terminate it + EX DE,HL ; Loop statement line number + LD (LINEAT),HL ; Set loop line number + LD L,C ; Set code string to loop + LD H,B + JP PUTFID ; Put back "FOR" and continue + +KILFOR: LD SP,HL ; Remove "FOR" block + LD HL,(BRKLIN) ; Code string after "NEXT" + LD A,(HL) ; Get next byte in code string + CP ',' ; More NEXTs ? + JP NZ,RUNCNT ; No - Do next statement + CALL GETCHR ; Position to index name + CALL NEXT1 ; Re-enter NEXT routine +; < will not RETurn to here , Exit to RUNCNT or Loop > + +GETNUM: CALL EVAL ; Get a numeric expression +TSTNUM: .BYTE 0F6H ; Clear carry (numeric) +TSTSTR: SCF ; Set carry (string) +CHKTYP: LD A,(TYPE) ; Check types match + ADC A,A ; Expected + actual + OR A ; Clear carry , set parity + RET PE ; Even parity - Types match + JP TMERR ; Different types - Error + +OPNPAR: CALL CHKSYN ; Make sure "(" follows + .BYTE "(" +EVAL: DEC HL ; Evaluate expression & save + LD D,0 ; Precedence value +EVAL1: PUSH DE ; Save precedence + LD C,1 + CALL CHKSTK ; Check for 1 level of stack + CALL OPRND ; Get next expression value +EVAL2: LD (NXTOPR),HL ; Save address of next operator +EVAL3: LD HL,(NXTOPR) ; Restore address of next opr + POP BC ; Precedence value and operator + LD A,B ; Get precedence value + CP 78H ; "AND" or "OR" ? + CALL NC,TSTNUM ; No - Make sure it's a number + LD A,(HL) ; Get next operator / function + LD D,0 ; Clear Last relation +RLTLP: SUB ZGTR ; ">" Token + JP C,FOPRND ; + - * / ^ AND OR - Test it + CP ZLTH+1-ZGTR ; < = > + JP NC,FOPRND ; Function - Call it + CP ZEQUAL-ZGTR ; "=" + RLA ; <- Test for legal + XOR D ; <- combinations of < = > + CP D ; <- by combining last token + LD D,A ; <- with current one + JP C,SNERR ; Error if "<<' '==" or ">>" + LD (CUROPR),HL ; Save address of current token + CALL GETCHR ; Get next character + JP RLTLP ; Treat the two as one + +FOPRND: LD A,D ; < = > found ? + OR A + JP NZ,TSTRED ; Yes - Test for reduction + LD A,(HL) ; Get operator token + LD (CUROPR),HL ; Save operator address + SUB ZPLUS ; Operator or function? + RET C ; Neither - Exit + CP ZOR+1-ZPLUS ; Is it + - * / ^ AND OR ? + RET NC ; No - Exit + LD E,A ; Coded operator + LD A,(TYPE) ; Get data type + DEC A ; FF = numeric , 00 = string + OR E ; Combine with coded operator + LD A,E ; Get coded operator + JP Z,CONCAT ; String concatenation + RLCA ; Times 2 + ADD A,E ; Times 3 + LD E,A ; To DE (D is 0) + LD HL,PRITAB ; Precedence table + ADD HL,DE ; To the operator concerned + LD A,B ; Last operator precedence + LD D,(HL) ; Get evaluation precedence + CP D ; Compare with eval precedence + RET NC ; Exit if higher precedence + INC HL ; Point to routine address + CALL TSTNUM ; Make sure it's a number + +STKTHS: PUSH BC ; Save last precedence & token + LD BC,EVAL3 ; Where to go on prec' break + PUSH BC ; Save on stack for return + LD B,E ; Save operator + LD C,D ; Save precedence + CALL STAKFP ; Move value to stack + LD E,B ; Restore operator + LD D,C ; Restore precedence + LD C,(HL) ; Get LSB of routine address + INC HL + LD B,(HL) ; Get MSB of routine address + INC HL + PUSH BC ; Save routine address + LD HL,(CUROPR) ; Address of current operator + JP EVAL1 ; Loop until prec' break + +OPRND: XOR A ; Get operand routine + LD (TYPE),A ; Set numeric expected + CALL GETCHR ; Get next character + LD E,MO ; ?MO Error + JP Z,ERROR ; No operand - Error + JP C,ASCTFP ; Number - Get value + CALL CHKLTR ; See if a letter + JP NC,CONVAR ; Letter - Find variable + CP '&' ; &H = HEX, &B = BINARY + JR NZ, NOTAMP + CALL GETCHR ; Get next character + CP 'H' ; Hex number indicated? [function added] + JP Z,HEXTFP ; Convert Hex to FPREG + CP 'B' ; Binary number indicated? [function added] + JP Z,BINTFP ; Convert Bin to FPREG + LD E,SN ; If neither then a ?SN Error + JP Z,ERROR ; +NOTAMP: CP ZPLUS ; '+' Token ? + JP Z,OPRND ; Yes - Look for operand + CP '.' ; '.' ? + JP Z,ASCTFP ; Yes - Create FP number + CP ZMINUS ; '-' Token ? + JP Z,MINUS ; Yes - Do minus + CP '"' ; Literal string ? + JP Z,QTSTR ; Get string terminated by '"' + CP ZNOT ; "NOT" Token ? + JP Z,EVNOT ; Yes - Eval NOT expression + CP ZFN ; "FN" Token ? + JP Z,DOFN ; Yes - Do FN routine + SUB ZSGN ; Is it a function? + JP NC,FNOFST ; Yes - Evaluate function +EVLPAR: CALL OPNPAR ; Evaluate expression in "()" + CALL CHKSYN ; Make sure ")" follows + .BYTE ")" + RET + +MINUS: LD D,7DH ; '-' precedence + CALL EVAL1 ; Evaluate until prec' break + LD HL,(NXTOPR) ; Get next operator address + PUSH HL ; Save next operator address + CALL INVSGN ; Negate value +RETNUM: CALL TSTNUM ; Make sure it's a number + POP HL ; Restore next operator address + RET + +CONVAR: CALL GETVAR ; Get variable address to DE +FRMEVL: PUSH HL ; Save code string address + EX DE,HL ; Variable address to HL + LD (FPREG),HL ; Save address of variable + LD A,(TYPE) ; Get type + OR A ; Numeric? + CALL Z,PHLTFP ; Yes - Move contents to FPREG + POP HL ; Restore code string address + RET + +FNOFST: LD B,0 ; Get address of function + RLCA ; Double function offset + LD C,A ; BC = Offset in function table + PUSH BC ; Save adjusted token value + CALL GETCHR ; Get next character + LD A,C ; Get adjusted token value + CP 2*(ZLEFT-ZSGN)-1; Adj' LEFT$,RIGHT$ or MID$ ? + JP C,FNVAL ; No - Do function + CALL OPNPAR ; Evaluate expression (X,... + CALL CHKSYN ; Make sure ',' follows + .BYTE ',' + CALL TSTSTR ; Make sure it's a string + EX DE,HL ; Save code string address + LD HL,(FPREG) ; Get address of string + EX (SP),HL ; Save address of string + PUSH HL ; Save adjusted token value + EX DE,HL ; Restore code string address + CALL GETINT ; Get integer 0-255 + EX DE,HL ; Save code string address + EX (SP),HL ; Save integer,HL = adj' token + JP GOFUNC ; Jump to string function + +FNVAL: CALL EVLPAR ; Evaluate expression + EX (SP),HL ; HL = Adjusted token value + LD DE,RETNUM ; Return number from function + PUSH DE ; Save on stack +GOFUNC: LD BC,FNCTAB ; Function routine addresses + ADD HL,BC ; Point to right address + LD C,(HL) ; Get LSB of address + INC HL ; + LD H,(HL) ; Get MSB of address + LD L,C ; Address to HL + JP (HL) ; Jump to function + +SGNEXP: DEC D ; Dee to flag negative exponent + CP ZMINUS ; '-' token ? + RET Z ; Yes - Return + CP '-' ; '-' ASCII ? + RET Z ; Yes - Return + INC D ; Inc to flag positive exponent + CP '+' ; '+' ASCII ? + RET Z ; Yes - Return + CP ZPLUS ; '+' token ? + RET Z ; Yes - Return + DEC HL ; DEC 'cos GETCHR INCs + RET ; Return "NZ" + +POR: .BYTE 0F6H ; Flag "OR" +PAND: XOR A ; Flag "AND" + PUSH AF ; Save "AND" / "OR" flag + CALL TSTNUM ; Make sure it's a number + CALL DEINT ; Get integer -32768 to 32767 + POP AF ; Restore "AND" / "OR" flag + EX DE,HL ; <- Get last + POP BC ; <- value + EX (SP),HL ; <- from + EX DE,HL ; <- stack + CALL FPBCDE ; Move last value to FPREG + PUSH AF ; Save "AND" / "OR" flag + CALL DEINT ; Get integer -32768 to 32767 + POP AF ; Restore "AND" / "OR" flag + POP BC ; Get value + LD A,C ; Get LSB + LD HL,ACPASS ; Address of save AC as current + JP NZ,POR1 ; Jump if OR + AND E ; "AND" LSBs + LD C,A ; Save LSB + LD A,B ; Get MBS + AND D ; "AND" MSBs + JP (HL) ; Save AC as current (ACPASS) + +POR1: OR E ; "OR" LSBs + LD C,A ; Save LSB + LD A,B ; Get MSB + OR D ; "OR" MSBs + JP (HL) ; Save AC as current (ACPASS) + +TSTRED: LD HL,CMPLOG ; Logical compare routine + LD A,(TYPE) ; Get data type + RRA ; Carry set = string + LD A,D ; Get last precedence value + RLA ; Times 2 plus carry + LD E,A ; To E + LD D,64H ; Relational precedence + LD A,B ; Get current precedence + CP D ; Compare with last + RET NC ; Eval if last was rel' or log' + JP STKTHS ; Stack this one and get next + +CMPLOG: .WORD CMPLG1 ; Compare two values / strings +CMPLG1: LD A,C ; Get data type + OR A + RRA + POP BC ; Get last expression to BCDE + POP DE + PUSH AF ; Save status + CALL CHKTYP ; Check that types match + LD HL,CMPRES ; Result to comparison + PUSH HL ; Save for RETurn + JP Z,CMPNUM ; Compare values if numeric + XOR A ; Compare two strings + LD (TYPE),A ; Set type to numeric + PUSH DE ; Save string name + CALL GSTRCU ; Get current string + LD A,(HL) ; Get length of string + INC HL + INC HL + LD C,(HL) ; Get LSB of address + INC HL + LD B,(HL) ; Get MSB of address + POP DE ; Restore string name + PUSH BC ; Save address of string + PUSH AF ; Save length of string + CALL GSTRDE ; Get second string + CALL LOADFP ; Get address of second string + POP AF ; Restore length of string 1 + LD D,A ; Length to D + POP HL ; Restore address of string 1 +CMPSTR: LD A,E ; Bytes of string 2 to do + OR D ; Bytes of string 1 to do + RET Z ; Exit if all bytes compared + LD A,D ; Get bytes of string 1 to do + SUB 1 + RET C ; Exit if end of string 1 + XOR A + CP E ; Bytes of string 2 to do + INC A + RET NC ; Exit if end of string 2 + DEC D ; Count bytes in string 1 + DEC E ; Count bytes in string 2 + LD A,(BC) ; Byte in string 2 + CP (HL) ; Compare to byte in string 1 + INC HL ; Move up string 1 + INC BC ; Move up string 2 + JP Z,CMPSTR ; Same - Try next bytes + CCF ; Flag difference (">" or "<") + JP FLGDIF ; "<" gives -1 , ">" gives +1 + +CMPRES: INC A ; Increment current value + ADC A,A ; Double plus carry + POP BC ; Get other value + AND B ; Combine them + ADD A,-1 ; Carry set if different + SBC A,A ; 00 - Equal , FF - Different + JP FLGREL ; Set current value & continue + +EVNOT: LD D,5AH ; Precedence value for "NOT" + CALL EVAL1 ; Eval until precedence break + CALL TSTNUM ; Make sure it's a number + CALL DEINT ; Get integer -32768 - 32767 + LD A,E ; Get LSB + CPL ; Invert LSB + LD C,A ; Save "NOT" of LSB + LD A,D ; Get MSB + CPL ; Invert MSB + CALL ACPASS ; Save AC as current + POP BC ; Clean up stack + JP EVAL3 ; Continue evaluation + +DIMRET: DEC HL ; DEC 'cos GETCHR INCs + CALL GETCHR ; Get next character + RET Z ; End of DIM statement + CALL CHKSYN ; Make sure ',' follows + .BYTE ',' +DIM: LD BC,DIMRET ; Return to "DIMRET" + PUSH BC ; Save on stack + .BYTE 0F6H ; Flag "Create" variable +GETVAR: XOR A ; Find variable address,to DE + LD (LCRFLG),A ; Set locate / create flag + LD B,(HL) ; Get First byte of name +GTFNAM: CALL CHKLTR ; See if a letter + JP C,SNERR ; ?SN Error if not a letter + XOR A + LD C,A ; Clear second byte of name + LD (TYPE),A ; Set type to numeric + CALL GETCHR ; Get next character + JP C,SVNAM2 ; Numeric - Save in name + CALL CHKLTR ; See if a letter + JP C,CHARTY ; Not a letter - Check type +SVNAM2: LD C,A ; Save second byte of name +ENDNAM: CALL GETCHR ; Get next character + JP C,ENDNAM ; Numeric - Get another + CALL CHKLTR ; See if a letter + JP NC,ENDNAM ; Letter - Get another +CHARTY: SUB '$' ; String variable? + JP NZ,NOTSTR ; No - Numeric variable + INC A ; A = 1 (string type) + LD (TYPE),A ; Set type to string + RRCA ; A = 80H , Flag for string + ADD A,C ; 2nd byte of name has bit 7 on + LD C,A ; Resave second byte on name + CALL GETCHR ; Get next character +NOTSTR: LD A,(FORFLG) ; Array name needed ? + DEC A + JP Z,ARLDSV ; Yes - Get array name + JP P,NSCFOR ; No array with "FOR" or "FN" + LD A,(HL) ; Get byte again + SUB '(' ; Subscripted variable? + JP Z,SBSCPT ; Yes - Sort out subscript + +NSCFOR: XOR A ; Simple variable + LD (FORFLG),A ; Clear "FOR" flag + PUSH HL ; Save code string address + LD D,B ; DE = Variable name to find + LD E,C + LD HL,(FNRGNM) ; FN argument name + CALL CPDEHL ; Is it the FN argument? + LD DE,FNARG ; Point to argument value + JP Z,POPHRT ; Yes - Return FN argument value + LD HL,(VAREND) ; End of variables + EX DE,HL ; Address of end of search + LD HL,(PROGND) ; Start of variables address +FNDVAR: CALL CPDEHL ; End of variable list table? + JP Z,CFEVAL ; Yes - Called from EVAL? + LD A,C ; Get second byte of name + SUB (HL) ; Compare with name in list + INC HL ; Move on to first byte + JP NZ,FNTHR ; Different - Find another + LD A,B ; Get first byte of name + SUB (HL) ; Compare with name in list +FNTHR: INC HL ; Move on to LSB of value + JP Z,RETADR ; Found - Return address + INC HL ; <- Skip + INC HL ; <- over + INC HL ; <- F.P. + INC HL ; <- value + JP FNDVAR ; Keep looking + +CFEVAL: POP HL ; Restore code string address + EX (SP),HL ; Get return address + PUSH DE ; Save address of variable + LD DE,FRMEVL ; Return address in EVAL + CALL CPDEHL ; Called from EVAL ? + POP DE ; Restore address of variable + JP Z,RETNUL ; Yes - Return null variable + EX (SP),HL ; Put back return + PUSH HL ; Save code string address + PUSH BC ; Save variable name + LD BC,6 ; 2 byte name plus 4 byte data + LD HL,(ARREND) ; End of arrays + PUSH HL ; Save end of arrays + ADD HL,BC ; Move up 6 bytes + POP BC ; Source address in BC + PUSH HL ; Save new end address + CALL MOVUP ; Move arrays up + POP HL ; Restore new end address + LD (ARREND),HL ; Set new end address + LD H,B ; End of variables to HL + LD L,C + LD (VAREND),HL ; Set new end address + +ZEROLP: DEC HL ; Back through to zero variable + LD (HL),0 ; Zero byte in variable + CALL CPDEHL ; Done them all? + JP NZ,ZEROLP ; No - Keep on going + POP DE ; Get variable name + LD (HL),E ; Store second character + INC HL + LD (HL),D ; Store first character + INC HL +RETADR: EX DE,HL ; Address of variable in DE + POP HL ; Restore code string address + RET + +RETNUL: LD (FPEXP),A ; Set result to zero + LD HL,ZERBYT ; Also set a null string + LD (FPREG),HL ; Save for EVAL + POP HL ; Restore code string address + RET + +SBSCPT: PUSH HL ; Save code string address + LD HL,(LCRFLG) ; Locate/Create and Type + EX (SP),HL ; Save and get code string + LD D,A ; Zero number of dimensions +SCPTLP: PUSH DE ; Save number of dimensions + PUSH BC ; Save array name + CALL FPSINT ; Get subscript (0-32767) + POP BC ; Restore array name + POP AF ; Get number of dimensions + EX DE,HL + EX (SP),HL ; Save subscript value + PUSH HL ; Save LCRFLG and TYPE + EX DE,HL + INC A ; Count dimensions + LD D,A ; Save in D + LD A,(HL) ; Get next byte in code string + CP ',' ; Comma (more to come)? + JP Z,SCPTLP ; Yes - More subscripts + CALL CHKSYN ; Make sure ")" follows + .BYTE ")" + LD (NXTOPR),HL ; Save code string address + POP HL ; Get LCRFLG and TYPE + LD (LCRFLG),HL ; Restore Locate/create & type + LD E,0 ; Flag not CSAVE* or CLOAD* + PUSH DE ; Save number of dimensions (D) + .BYTE 11H ; Skip "PUSH HL" and "PUSH AF' + +ARLDSV: PUSH HL ; Save code string address + PUSH AF ; A = 00 , Flags set = Z,N + LD HL,(VAREND) ; Start of arrays + .BYTE 3EH ; Skip "ADD HL,DE" +FNDARY: ADD HL,DE ; Move to next array start + EX DE,HL + LD HL,(ARREND) ; End of arrays + EX DE,HL ; Current array pointer + CALL CPDEHL ; End of arrays found? + JP Z,CREARY ; Yes - Create array + LD A,(HL) ; Get second byte of name + CP C ; Compare with name given + INC HL ; Move on + JP NZ,NXTARY ; Different - Find next array + LD A,(HL) ; Get first byte of name + CP B ; Compare with name given +NXTARY: INC HL ; Move on + LD E,(HL) ; Get LSB of next array address + INC HL + LD D,(HL) ; Get MSB of next array address + INC HL + JP NZ,FNDARY ; Not found - Keep looking + LD A,(LCRFLG) ; Found Locate or Create it? + OR A + JP NZ,DDERR ; Create - ?DD Error + POP AF ; Locate - Get number of dim'ns + LD B,H ; BC Points to array dim'ns + LD C,L + JP Z,POPHRT ; Jump if array load/save + SUB (HL) ; Same number of dimensions? + JP Z,FINDEL ; Yes - Find element +BSERR: LD E,BS ; ?BS Error + JP ERROR ; Output error + +CREARY: LD DE,4 ; 4 Bytes per entry + POP AF ; Array to save or 0 dim'ns? + JP Z,FCERR ; Yes - ?FC Error + LD (HL),C ; Save second byte of name + INC HL + LD (HL),B ; Save first byte of name + INC HL + LD C,A ; Number of dimensions to C + CALL CHKSTK ; Check if enough memory + INC HL ; Point to number of dimensions + INC HL + LD (CUROPR),HL ; Save address of pointer + LD (HL),C ; Set number of dimensions + INC HL + LD A,(LCRFLG) ; Locate of Create? + RLA ; Carry set = Create + LD A,C ; Get number of dimensions +CRARLP: LD BC,10+1 ; Default dimension size 10 + JP NC,DEFSIZ ; Locate - Set default size + POP BC ; Get specified dimension size + INC BC ; Include zero element +DEFSIZ: LD (HL),C ; Save LSB of dimension size + INC HL + LD (HL),B ; Save MSB of dimension size + INC HL + PUSH AF ; Save num' of dim'ns an status + PUSH HL ; Save address of dim'n size + CALL MLDEBC ; Multiply DE by BC to find + EX DE,HL ; amount of mem needed (to DE) + POP HL ; Restore address of dimension + POP AF ; Restore number of dimensions + DEC A ; Count them + JP NZ,CRARLP ; Do next dimension if more + PUSH AF ; Save locate/create flag + LD B,D ; MSB of memory needed + LD C,E ; LSB of memory needed + EX DE,HL + ADD HL,DE ; Add bytes to array start + JP C,OMERR ; Too big - Error + CALL ENFMEM ; See if enough memory + LD (ARREND),HL ; Save new end of array + +ZERARY: DEC HL ; Back through array data + LD (HL),0 ; Set array element to zero + CALL CPDEHL ; All elements zeroed? + JP NZ,ZERARY ; No - Keep on going + INC BC ; Number of bytes + 1 + LD D,A ; A=0 + LD HL,(CUROPR) ; Get address of array + LD E,(HL) ; Number of dimensions + EX DE,HL ; To HL + ADD HL,HL ; Two bytes per dimension size + ADD HL,BC ; Add number of bytes + EX DE,HL ; Bytes needed to DE + DEC HL + DEC HL + LD (HL),E ; Save LSB of bytes needed + INC HL + LD (HL),D ; Save MSB of bytes needed + INC HL + POP AF ; Locate / Create? + JP C,ENDDIM ; A is 0 , End if create +FINDEL: LD B,A ; Find array element + LD C,A + LD A,(HL) ; Number of dimensions + INC HL + .BYTE 16H ; Skip "POP HL" +FNDELP: POP HL ; Address of next dim' size + LD E,(HL) ; Get LSB of dim'n size + INC HL + LD D,(HL) ; Get MSB of dim'n size + INC HL + EX (SP),HL ; Save address - Get index + PUSH AF ; Save number of dim'ns + CALL CPDEHL ; Dimension too large? + JP NC,BSERR ; Yes - ?BS Error + PUSH HL ; Save index + CALL MLDEBC ; Multiply previous by size + POP DE ; Index supplied to DE + ADD HL,DE ; Add index to pointer + POP AF ; Number of dimensions + DEC A ; Count them + LD B,H ; MSB of pointer + LD C,L ; LSB of pointer + JP NZ,FNDELP ; More - Keep going + ADD HL,HL ; 4 Bytes per element + ADD HL,HL + POP BC ; Start of array + ADD HL,BC ; Point to element + EX DE,HL ; Address of element to DE +ENDDIM: LD HL,(NXTOPR) ; Got code string address + RET + +FRE: LD HL,(ARREND) ; Start of free memory + EX DE,HL ; To DE + LD HL,0 ; End of free memory + ADD HL,SP ; Current stack value + LD A,(TYPE) ; Dummy argument type + OR A + JP Z,FRENUM ; Numeric - Free variable space + CALL GSTRCU ; Current string to pool + CALL GARBGE ; Garbage collection + LD HL,(STRSPC) ; Bottom of string space in use + EX DE,HL ; To DE + LD HL,(STRBOT) ; Bottom of string space +FRENUM: LD A,L ; Get LSB of end + SUB E ; Subtract LSB of beginning + LD C,A ; Save difference if C + LD A,H ; Get MSB of end + SBC A,D ; Subtract MSB of beginning +ACPASS: LD B,C ; Return integer AC +ABPASS: LD D,B ; Return integer AB + LD E,0 + LD HL,TYPE ; Point to type + LD (HL),E ; Set type to numeric + LD B,80H+16 ; 16 bit integer + JP RETINT ; Return the integr + +POS: LD A,(CURPOS) ; Get cursor position +PASSA: LD B,A ; Put A into AB + XOR A ; Zero A + JP ABPASS ; Return integer AB + +DEF: CALL CHEKFN ; Get "FN" and name + CALL IDTEST ; Test for illegal direct + LD BC,DATA ; To get next statement + PUSH BC ; Save address for RETurn + PUSH DE ; Save address of function ptr + CALL CHKSYN ; Make sure "(" follows + .BYTE "(" + CALL GETVAR ; Get argument variable name + PUSH HL ; Save code string address + EX DE,HL ; Argument address to HL + DEC HL + LD D,(HL) ; Get first byte of arg name + DEC HL + LD E,(HL) ; Get second byte of arg name + POP HL ; Restore code string address + CALL TSTNUM ; Make sure numeric argument + CALL CHKSYN ; Make sure ")" follows + .BYTE ")" + CALL CHKSYN ; Make sure "=" follows + .BYTE ZEQUAL ; "=" token + LD B,H ; Code string address to BC + LD C,L + EX (SP),HL ; Save code str , Get FN ptr + LD (HL),C ; Save LSB of FN code string + INC HL + LD (HL),B ; Save MSB of FN code string + JP SVSTAD ; Save address and do function + +DOFN: CALL CHEKFN ; Make sure FN follows + PUSH DE ; Save function pointer address + CALL EVLPAR ; Evaluate expression in "()" + CALL TSTNUM ; Make sure numeric result + EX (SP),HL ; Save code str , Get FN ptr + LD E,(HL) ; Get LSB of FN code string + INC HL + LD D,(HL) ; Get MSB of FN code string + INC HL + LD A,D ; And function DEFined? + OR E + JP Z,UFERR ; No - ?UF Error + LD A,(HL) ; Get LSB of argument address + INC HL + LD H,(HL) ; Get MSB of argument address + LD L,A ; HL = Arg variable address + PUSH HL ; Save it + LD HL,(FNRGNM) ; Get old argument name + EX (SP),HL ; ; Save old , Get new + LD (FNRGNM),HL ; Set new argument name + LD HL,(FNARG+2) ; Get LSB,NLSB of old arg value + PUSH HL ; Save it + LD HL,(FNARG) ; Get MSB,EXP of old arg value + PUSH HL ; Save it + LD HL,FNARG ; HL = Value of argument + PUSH DE ; Save FN code string address + CALL FPTHL ; Move FPREG to argument + POP HL ; Get FN code string address + CALL GETNUM ; Get value from function + DEC HL ; DEC 'cos GETCHR INCs + CALL GETCHR ; Get next character + JP NZ,SNERR ; Bad character in FN - Error + POP HL ; Get MSB,EXP of old arg + LD (FNARG),HL ; Restore it + POP HL ; Get LSB,NLSB of old arg + LD (FNARG+2),HL ; Restore it + POP HL ; Get name of old arg + LD (FNRGNM),HL ; Restore it + POP HL ; Restore code string address + RET + +IDTEST: PUSH HL ; Save code string address + LD HL,(LINEAT) ; Get current line number + INC HL ; -1 means direct statement + LD A,H + OR L + POP HL ; Restore code string address + RET NZ ; Return if in program + LD E,ID ; ?ID Error + JP ERROR + +CHEKFN: CALL CHKSYN ; Make sure FN follows + .BYTE ZFN ; "FN" token + LD A,80H + LD (FORFLG),A ; Flag FN name to find + OR (HL) ; FN name has bit 7 set + LD B,A ; in first byte of name + CALL GTFNAM ; Get FN name + JP TSTNUM ; Make sure numeric function + +STR: CALL TSTNUM ; Make sure it's a number + CALL NUMASC ; Turn number into text +STR1: CALL CRTST ; Create string entry for it + CALL GSTRCU ; Current string to pool + LD BC,TOPOOL ; Save in string pool + PUSH BC ; Save address on stack + +SAVSTR: LD A,(HL) ; Get string length + INC HL + INC HL + PUSH HL ; Save pointer to string + CALL TESTR ; See if enough string space + POP HL ; Restore pointer to string + LD C,(HL) ; Get LSB of address + INC HL + LD B,(HL) ; Get MSB of address + CALL CRTMST ; Create string entry + PUSH HL ; Save pointer to MSB of addr + LD L,A ; Length of string + CALL TOSTRA ; Move to string area + POP DE ; Restore pointer to MSB + RET + +MKTMST: CALL TESTR ; See if enough string space +CRTMST: LD HL,TMPSTR ; Temporary string + PUSH HL ; Save it + LD (HL),A ; Save length of string + INC HL +SVSTAD: INC HL + LD (HL),E ; Save LSB of address + INC HL + LD (HL),D ; Save MSB of address + POP HL ; Restore pointer + RET + +CRTST: DEC HL ; DEC - INCed after +QTSTR: LD B,'"' ; Terminating quote + LD D,B ; Quote to D +DTSTR: PUSH HL ; Save start + LD C,-1 ; Set counter to -1 +QTSTLP: INC HL ; Move on + LD A,(HL) ; Get byte + INC C ; Count bytes + OR A ; End of line? + JP Z,CRTSTE ; Yes - Create string entry + CP D ; Terminator D found? + JP Z,CRTSTE ; Yes - Create string entry + CP B ; Terminator B found? + JP NZ,QTSTLP ; No - Keep looking +CRTSTE: CP '"' ; End with '"'? + CALL Z,GETCHR ; Yes - Get next character + EX (SP),HL ; Starting quote + INC HL ; First byte of string + EX DE,HL ; To DE + LD A,C ; Get length + CALL CRTMST ; Create string entry +TSTOPL: LD DE,TMPSTR ; Temporary string + LD HL,(TMSTPT) ; Temporary string pool pointer + LD (FPREG),HL ; Save address of string ptr + LD A,1 + LD (TYPE),A ; Set type to string + CALL DETHL4 ; Move string to pool + CALL CPDEHL ; Out of string pool? + LD (TMSTPT),HL ; Save new pointer + POP HL ; Restore code string address + LD A,(HL) ; Get next code byte + RET NZ ; Return if pool OK + LD E,ST ; ?ST Error + JP ERROR ; String pool overflow + +PRNUMS: INC HL ; Skip leading space +PRS: CALL CRTST ; Create string entry for it +PRS1: CALL GSTRCU ; Current string to pool + CALL LOADFP ; Move string block to BCDE + INC E ; Length + 1 +PRSLP: DEC E ; Count characters + RET Z ; End of string + LD A,(BC) ; Get byte to output + CALL OUTC ; Output character in A + CP CR ; Return? + CALL Z,DONULL ; Yes - Do nulls + INC BC ; Next byte in string + JP PRSLP ; More characters to output + +TESTR: OR A ; Test if enough room + .BYTE 0EH ; No garbage collection done +GRBDON: POP AF ; Garbage collection done + PUSH AF ; Save status + LD HL,(STRSPC) ; Bottom of string space in use + EX DE,HL ; To DE + LD HL,(STRBOT) ; Bottom of string area + CPL ; Negate length (Top down) + LD C,A ; -Length to BC + LD B,-1 ; BC = -ve length of string + ADD HL,BC ; Add to bottom of space in use + INC HL ; Plus one for 2's complement + CALL CPDEHL ; Below string RAM area? + JP C,TESTOS ; Tidy up if not done else err + LD (STRBOT),HL ; Save new bottom of area + INC HL ; Point to first byte of string + EX DE,HL ; Address to DE +POPAF: POP AF ; Throw away status push + RET + +TESTOS: POP AF ; Garbage collect been done? + LD E,OS ; ?OS Error + JP Z,ERROR ; Yes - Not enough string apace + CP A ; Flag garbage collect done + PUSH AF ; Save status + LD BC,GRBDON ; Garbage collection done + PUSH BC ; Save for RETurn +GARBGE: LD HL,(LSTRAM) ; Get end of RAM pointer +GARBLP: LD (STRBOT),HL ; Reset string pointer + LD HL,0 + PUSH HL ; Flag no string found + LD HL,(STRSPC) ; Get bottom of string space + PUSH HL ; Save bottom of string space + LD HL,TMSTPL ; Temporary string pool +GRBLP: EX DE,HL + LD HL,(TMSTPT) ; Temporary string pool pointer + EX DE,HL + CALL CPDEHL ; Temporary string pool done? + LD BC,GRBLP ; Loop until string pool done + JP NZ,STPOOL ; No - See if in string area + LD HL,(PROGND) ; Start of simple variables +SMPVAR: EX DE,HL + LD HL,(VAREND) ; End of simple variables + EX DE,HL + CALL CPDEHL ; All simple strings done? + JP Z,ARRLP ; Yes - Do string arrays + LD A,(HL) ; Get type of variable + INC HL + INC HL + OR A ; "S" flag set if string + CALL STRADD ; See if string in string area + JP SMPVAR ; Loop until simple ones done + +GNXARY: POP BC ; Scrap address of this array +ARRLP: EX DE,HL + LD HL,(ARREND) ; End of string arrays + EX DE,HL + CALL CPDEHL ; All string arrays done? + JP Z,SCNEND ; Yes - Move string if found + CALL LOADFP ; Get array name to BCDE + LD A,E ; Get type of array + PUSH HL ; Save address of num of dim'ns + ADD HL,BC ; Start of next array + OR A ; Test type of array + JP P,GNXARY ; Numeric array - Ignore it + LD (CUROPR),HL ; Save address of next array + POP HL ; Get address of num of dim'ns + LD C,(HL) ; BC = Number of dimensions + LD B,0 + ADD HL,BC ; Two bytes per dimension size + ADD HL,BC + INC HL ; Plus one for number of dim'ns +GRBARY: EX DE,HL + LD HL,(CUROPR) ; Get address of next array + EX DE,HL + CALL CPDEHL ; Is this array finished? + JP Z,ARRLP ; Yes - Get next one + LD BC,GRBARY ; Loop until array all done +STPOOL: PUSH BC ; Save return address + OR 80H ; Flag string type +STRADD: LD A,(HL) ; Get string length + INC HL + INC HL + LD E,(HL) ; Get LSB of string address + INC HL + LD D,(HL) ; Get MSB of string address + INC HL + RET P ; Not a string - Return + OR A ; Set flags on string length + RET Z ; Null string - Return + LD B,H ; Save variable pointer + LD C,L + LD HL,(STRBOT) ; Bottom of new area + CALL CPDEHL ; String been done? + LD H,B ; Restore variable pointer + LD L,C + RET C ; String done - Ignore + POP HL ; Return address + EX (SP),HL ; Lowest available string area + CALL CPDEHL ; String within string area? + EX (SP),HL ; Lowest available string area + PUSH HL ; Re-save return address + LD H,B ; Restore variable pointer + LD L,C + RET NC ; Outside string area - Ignore + POP BC ; Get return , Throw 2 away + POP AF ; + POP AF ; + PUSH HL ; Save variable pointer + PUSH DE ; Save address of current + PUSH BC ; Put back return address + RET ; Go to it + +SCNEND: POP DE ; Addresses of strings + POP HL ; + LD A,L ; HL = 0 if no more to do + OR H + RET Z ; No more to do - Return + DEC HL + LD B,(HL) ; MSB of address of string + DEC HL + LD C,(HL) ; LSB of address of string + PUSH HL ; Save variable address + DEC HL + DEC HL + LD L,(HL) ; HL = Length of string + LD H,0 + ADD HL,BC ; Address of end of string+1 + LD D,B ; String address to DE + LD E,C + DEC HL ; Last byte in string + LD B,H ; Address to BC + LD C,L + LD HL,(STRBOT) ; Current bottom of string area + CALL MOVSTR ; Move string to new address + POP HL ; Restore variable address + LD (HL),C ; Save new LSB of address + INC HL + LD (HL),B ; Save new MSB of address + LD L,C ; Next string area+1 to HL + LD H,B + DEC HL ; Next string area address + JP GARBLP ; Look for more strings + +CONCAT: PUSH BC ; Save prec' opr & code string + PUSH HL ; + LD HL,(FPREG) ; Get first string + EX (SP),HL ; Save first string + CALL OPRND ; Get second string + EX (SP),HL ; Restore first string + CALL TSTSTR ; Make sure it's a string + LD A,(HL) ; Get length of second string + PUSH HL ; Save first string + LD HL,(FPREG) ; Get second string + PUSH HL ; Save second string + ADD A,(HL) ; Add length of second string + LD E,LS ; ?LS Error + JP C,ERROR ; String too long - Error + CALL MKTMST ; Make temporary string + POP DE ; Get second string to DE + CALL GSTRDE ; Move to string pool if needed + EX (SP),HL ; Get first string + CALL GSTRHL ; Move to string pool if needed + PUSH HL ; Save first string + LD HL,(TMPSTR+2) ; Temporary string address + EX DE,HL ; To DE + CALL SSTSA ; First string to string area + CALL SSTSA ; Second string to string area + LD HL,EVAL2 ; Return to evaluation loop + EX (SP),HL ; Save return,get code string + PUSH HL ; Save code string address + JP TSTOPL ; To temporary string to pool + +SSTSA: POP HL ; Return address + EX (SP),HL ; Get string block,save return + LD A,(HL) ; Get length of string + INC HL + INC HL + LD C,(HL) ; Get LSB of string address + INC HL + LD B,(HL) ; Get MSB of string address + LD L,A ; Length to L +TOSTRA: INC L ; INC - DECed after +TSALP: DEC L ; Count bytes moved + RET Z ; End of string - Return + LD A,(BC) ; Get source + LD (DE),A ; Save destination + INC BC ; Next source + INC DE ; Next destination + JP TSALP ; Loop until string moved + +GETSTR: CALL TSTSTR ; Make sure it's a string +GSTRCU: LD HL,(FPREG) ; Get current string +GSTRHL: EX DE,HL ; Save DE +GSTRDE: CALL BAKTMP ; Was it last tmp-str? + EX DE,HL ; Restore DE + RET NZ ; No - Return + PUSH DE ; Save string + LD D,B ; String block address to DE + LD E,C + DEC DE ; Point to length + LD C,(HL) ; Get string length + LD HL,(STRBOT) ; Current bottom of string area + CALL CPDEHL ; Last one in string area? + JP NZ,POPHL ; No - Return + LD B,A ; Clear B (A=0) + ADD HL,BC ; Remove string from str' area + LD (STRBOT),HL ; Save new bottom of str' area +POPHL: POP HL ; Restore string + RET + +BAKTMP: LD HL,(TMSTPT) ; Get temporary string pool top + DEC HL ; Back + LD B,(HL) ; Get MSB of address + DEC HL ; Back + LD C,(HL) ; Get LSB of address + DEC HL ; Back + DEC HL ; Back + CALL CPDEHL ; String last in string pool? + RET NZ ; Yes - Leave it + LD (TMSTPT),HL ; Save new string pool top + RET + +LEN: LD BC,PASSA ; To return integer A + PUSH BC ; Save address +GETLEN: CALL GETSTR ; Get string and its length + XOR A + LD D,A ; Clear D + LD (TYPE),A ; Set type to numeric + LD A,(HL) ; Get length of string + OR A ; Set status flags + RET + +ASC: LD BC,PASSA ; To return integer A + PUSH BC ; Save address +GTFLNM: CALL GETLEN ; Get length of string + JP Z,FCERR ; Null string - Error + INC HL + INC HL + LD E,(HL) ; Get LSB of address + INC HL + LD D,(HL) ; Get MSB of address + LD A,(DE) ; Get first byte of string + RET + +CHR: LD A,1 ; One character string + CALL MKTMST ; Make a temporary string + CALL MAKINT ; Make it integer A + LD HL,(TMPSTR+2) ; Get address of string + LD (HL),E ; Save character +TOPOOL: POP BC ; Clean up stack + JP TSTOPL ; Temporary string to pool + +LEFT: CALL LFRGNM ; Get number and ending ")" + XOR A ; Start at first byte in string +RIGHT1: EX (SP),HL ; Save code string,Get string + LD C,A ; Starting position in string +MID1: PUSH HL ; Save string block address + LD A,(HL) ; Get length of string + CP B ; Compare with number given + JP C,ALLFOL ; All following bytes required + LD A,B ; Get new length + .BYTE 11H ; Skip "LD C,0" +ALLFOL: LD C,0 ; First byte of string + PUSH BC ; Save position in string + CALL TESTR ; See if enough string space + POP BC ; Get position in string + POP HL ; Restore string block address + PUSH HL ; And re-save it + INC HL + INC HL + LD B,(HL) ; Get LSB of address + INC HL + LD H,(HL) ; Get MSB of address + LD L,B ; HL = address of string + LD B,0 ; BC = starting address + ADD HL,BC ; Point to that byte + LD B,H ; BC = source string + LD C,L + CALL CRTMST ; Create a string entry + LD L,A ; Length of new string + CALL TOSTRA ; Move string to string area + POP DE ; Clear stack + CALL GSTRDE ; Move to string pool if needed + JP TSTOPL ; Temporary string to pool + +RIGHT: CALL LFRGNM ; Get number and ending ")" + POP DE ; Get string length + PUSH DE ; And re-save + LD A,(DE) ; Get length + SUB B ; Move back N bytes + JP RIGHT1 ; Go and get sub-string + +MID: EX DE,HL ; Get code string address + LD A,(HL) ; Get next byte ',' or ")" + CALL MIDNUM ; Get number supplied + INC B ; Is it character zero? + DEC B + JP Z,FCERR ; Yes - Error + PUSH BC ; Save starting position + LD E,255 ; All of string + CP ')' ; Any length given? + JP Z,RSTSTR ; No - Rest of string + CALL CHKSYN ; Make sure ',' follows + .BYTE ',' + CALL GETINT ; Get integer 0-255 +RSTSTR: CALL CHKSYN ; Make sure ")" follows + .BYTE ")" + POP AF ; Restore starting position + EX (SP),HL ; Get string,8ave code string + LD BC,MID1 ; Continuation of MID$ routine + PUSH BC ; Save for return + DEC A ; Starting position-1 + CP (HL) ; Compare with length + LD B,0 ; Zero bytes length + RET NC ; Null string if start past end + LD C,A ; Save starting position-1 + LD A,(HL) ; Get length of string + SUB C ; Subtract start + CP E ; Enough string for it? + LD B,A ; Save maximum length available + RET C ; Truncate string if needed + LD B,E ; Set specified length + RET ; Go and create string + +VAL: CALL GETLEN ; Get length of string + JP Z,RESZER ; Result zero + LD E,A ; Save length + INC HL + INC HL + LD A,(HL) ; Get LSB of address + INC HL + LD H,(HL) ; Get MSB of address + LD L,A ; HL = String address + PUSH HL ; Save string address + ADD HL,DE + LD B,(HL) ; Get end of string+1 byte + LD (HL),D ; Zero it to terminate + EX (SP),HL ; Save string end,get start + PUSH BC ; Save end+1 byte + LD A,(HL) ; Get starting byte + CP '$' ; Hex number indicated? [function added] + JP NZ,VAL1 + CALL HEXTFP ; Convert Hex to FPREG + JR VAL3 +VAL1: CP '%' ; Binary number indicated? [function added] + JP NZ,VAL2 + CALL BINTFP ; Convert Bin to FPREG + JR VAL3 +VAL2: CALL ASCTFP ; Convert ASCII string to FP +VAL3: POP BC ; Restore end+1 byte + POP HL ; Restore end+1 address + LD (HL),B ; Put back original byte + RET + +LFRGNM: EX DE,HL ; Code string address to HL + CALL CHKSYN ; Make sure ")" follows + .BYTE ")" +MIDNUM: POP BC ; Get return address + POP DE ; Get number supplied + PUSH BC ; Re-save return address + LD B,E ; Number to B + RET + +INP: CALL MAKINT ; Make it integer A + LD (INPORT),A ; Set input port + CALL INPSUB ; Get input from port + JP PASSA ; Return integer A + +POUT: CALL SETIO ; Set up port number + JP OUTSUB ; Output data and return + +WAIT: CALL SETIO ; Set up port number + PUSH AF ; Save AND mask + LD E,0 ; Assume zero if none given + DEC HL ; DEC 'cos GETCHR INCs + CALL GETCHR ; Get next character + JP Z,NOXOR ; No XOR byte given + CALL CHKSYN ; Make sure ',' follows + .BYTE ',' + CALL GETINT ; Get integer 0-255 to XOR with +NOXOR: POP BC ; Restore AND mask +WAITLP: CALL INPSUB ; Get input + XOR E ; Flip selected bits + AND B ; Result non-zero? + JP Z,WAITLP ; No = keep waiting + RET + +SETIO: CALL GETINT ; Get integer 0-255 + LD (INPORT),A ; Set input port + LD (OTPORT),A ; Set output port + CALL CHKSYN ; Make sure ',' follows + .BYTE ',' + JP GETINT ; Get integer 0-255 and return + +FNDNUM: CALL GETCHR ; Get next character +GETINT: CALL GETNUM ; Get a number from 0 to 255 +MAKINT: CALL DEPINT ; Make sure value 0 - 255 + LD A,D ; Get MSB of number + OR A ; Zero? + JP NZ,FCERR ; No - Error + DEC HL ; DEC 'cos GETCHR INCs + CALL GETCHR ; Get next character + LD A,E ; Get number to A + RET + +PEEK: CALL DEINT ; Get memory address + LD A,(DE) ; Get byte in memory + JP PASSA ; Return integer A + +POKE: CALL GETNUM ; Get memory address + CALL DEINT ; Get integer -32768 to 3276 + PUSH DE ; Save memory address + CALL CHKSYN ; Make sure ',' follows + .BYTE ',' + CALL GETINT ; Get integer 0-255 + POP DE ; Restore memory address + LD (DE),A ; Load it into memory + RET + +ROUND: LD HL,HALF ; Add 0.5 to FPREG +ADDPHL: CALL LOADFP ; Load FP at (HL) to BCDE + JP FPADD ; Add BCDE to FPREG + +SUBPHL: CALL LOADFP ; FPREG = -FPREG + number at HL + .BYTE 21H ; Skip "POP BC" and "POP DE" +PSUB: POP BC ; Get FP number from stack + POP DE +SUBCDE: CALL INVSGN ; Negate FPREG +FPADD: LD A,B ; Get FP exponent + OR A ; Is number zero? + RET Z ; Yes - Nothing to add + LD A,(FPEXP) ; Get FPREG exponent + OR A ; Is this number zero? + JP Z,FPBCDE ; Yes - Move BCDE to FPREG + SUB B ; BCDE number larger? + JP NC,NOSWAP ; No - Don't swap them + CPL ; Two's complement + INC A ; FP exponent + EX DE,HL + CALL STAKFP ; Put FPREG on stack + EX DE,HL + CALL FPBCDE ; Move BCDE to FPREG + POP BC ; Restore number from stack + POP DE +NOSWAP: CP 24+1 ; Second number insignificant? + RET NC ; Yes - First number is result + PUSH AF ; Save number of bits to scale + CALL SIGNS ; Set MSBs & sign of result + LD H,A ; Save sign of result + POP AF ; Restore scaling factor + CALL SCALE ; Scale BCDE to same exponent + OR H ; Result to be positive? + LD HL,FPREG ; Point to FPREG + JP P,MINCDE ; No - Subtract FPREG from CDE + CALL PLUCDE ; Add FPREG to CDE + JP NC,RONDUP ; No overflow - Round it up + INC HL ; Point to exponent + INC (HL) ; Increment it + JP Z,OVERR ; Number overflowed - Error + LD L,1 ; 1 bit to shift right + CALL SHRT1 ; Shift result right + JP RONDUP ; Round it up + +MINCDE: XOR A ; Clear A and carry + SUB B ; Negate exponent + LD B,A ; Re-save exponent + LD A,(HL) ; Get LSB of FPREG + SBC A, E ; Subtract LSB of BCDE + LD E,A ; Save LSB of BCDE + INC HL + LD A,(HL) ; Get NMSB of FPREG + SBC A,D ; Subtract NMSB of BCDE + LD D,A ; Save NMSB of BCDE + INC HL + LD A,(HL) ; Get MSB of FPREG + SBC A,C ; Subtract MSB of BCDE + LD C,A ; Save MSB of BCDE +CONPOS: CALL C,COMPL ; Overflow - Make it positive + +BNORM: LD L,B ; L = Exponent + LD H,E ; H = LSB + XOR A +BNRMLP: LD B,A ; Save bit count + LD A,C ; Get MSB + OR A ; Is it zero? + JP NZ,PNORM ; No - Do it bit at a time + LD C,D ; MSB = NMSB + LD D,H ; NMSB= LSB + LD H,L ; LSB = VLSB + LD L,A ; VLSB= 0 + LD A,B ; Get exponent + SUB 8 ; Count 8 bits + CP -24-8 ; Was number zero? + JP NZ,BNRMLP ; No - Keep normalising +RESZER: XOR A ; Result is zero +SAVEXP: LD (FPEXP),A ; Save result as zero + RET + +NORMAL: DEC B ; Count bits + ADD HL,HL ; Shift HL left + LD A,D ; Get NMSB + RLA ; Shift left with last bit + LD D,A ; Save NMSB + LD A,C ; Get MSB + ADC A,A ; Shift left with last bit + LD C,A ; Save MSB +PNORM: JP P,NORMAL ; Not done - Keep going + LD A,B ; Number of bits shifted + LD E,H ; Save HL in EB + LD B,L + OR A ; Any shifting done? + JP Z,RONDUP ; No - Round it up + LD HL,FPEXP ; Point to exponent + ADD A,(HL) ; Add shifted bits + LD (HL),A ; Re-save exponent + JP NC,RESZER ; Underflow - Result is zero + RET Z ; Result is zero +RONDUP: LD A,B ; Get VLSB of number +RONDB: LD HL,FPEXP ; Point to exponent + OR A ; Any rounding? + CALL M,FPROND ; Yes - Round number up + LD B,(HL) ; B = Exponent + INC HL + LD A,(HL) ; Get sign of result + AND 10000000B ; Only bit 7 needed + XOR C ; Set correct sign + LD C,A ; Save correct sign in number + JP FPBCDE ; Move BCDE to FPREG + +FPROND: INC E ; Round LSB + RET NZ ; Return if ok + INC D ; Round NMSB + RET NZ ; Return if ok + INC C ; Round MSB + RET NZ ; Return if ok + LD C,80H ; Set normal value + INC (HL) ; Increment exponent + RET NZ ; Return if ok + JP OVERR ; Overflow error + +PLUCDE: LD A,(HL) ; Get LSB of FPREG + ADD A,E ; Add LSB of BCDE + LD E,A ; Save LSB of BCDE + INC HL + LD A,(HL) ; Get NMSB of FPREG + ADC A,D ; Add NMSB of BCDE + LD D,A ; Save NMSB of BCDE + INC HL + LD A,(HL) ; Get MSB of FPREG + ADC A,C ; Add MSB of BCDE + LD C,A ; Save MSB of BCDE + RET + +COMPL: LD HL,SGNRES ; Sign of result + LD A,(HL) ; Get sign of result + CPL ; Negate it + LD (HL),A ; Put it back + XOR A + LD L,A ; Set L to zero + SUB B ; Negate exponent,set carry + LD B,A ; Re-save exponent + LD A,L ; Load zero + SBC A,E ; Negate LSB + LD E,A ; Re-save LSB + LD A,L ; Load zero + SBC A,D ; Negate NMSB + LD D,A ; Re-save NMSB + LD A,L ; Load zero + SBC A,C ; Negate MSB + LD C,A ; Re-save MSB + RET + +SCALE: LD B,0 ; Clear underflow +SCALLP: SUB 8 ; 8 bits (a whole byte)? + JP C,SHRITE ; No - Shift right A bits + LD B,E ; <- Shift + LD E,D ; <- right + LD D,C ; <- eight + LD C,0 ; <- bits + JP SCALLP ; More bits to shift + +SHRITE: ADD A,8+1 ; Adjust count + LD L,A ; Save bits to shift +SHRLP: XOR A ; Flag for all done + DEC L ; All shifting done? + RET Z ; Yes - Return + LD A,C ; Get MSB +SHRT1: RRA ; Shift it right + LD C,A ; Re-save + LD A,D ; Get NMSB + RRA ; Shift right with last bit + LD D,A ; Re-save it + LD A,E ; Get LSB + RRA ; Shift right with last bit + LD E,A ; Re-save it + LD A,B ; Get underflow + RRA ; Shift right with last bit + LD B,A ; Re-save underflow + JP SHRLP ; More bits to do + +UNITY: .BYTE 000H,000H,000H,081H ; 1.00000 + +LOGTAB: .BYTE 3 ; Table used by LOG + .BYTE 0AAH,056H,019H,080H ; 0.59898 + .BYTE 0F1H,022H,076H,080H ; 0.96147 + .BYTE 045H,0AAH,038H,082H ; 2.88539 + +LOG: CALL TSTSGN ; Test sign of value + OR A + JP PE,FCERR ; ?FC Error if <= zero + LD HL,FPEXP ; Point to exponent + LD A,(HL) ; Get exponent + LD BC,8035H ; BCDE = SQR(1/2) + LD DE,04F3H + SUB B ; Scale value to be < 1 + PUSH AF ; Save scale factor + LD (HL),B ; Save new exponent + PUSH DE ; Save SQR(1/2) + PUSH BC + CALL FPADD ; Add SQR(1/2) to value + POP BC ; Restore SQR(1/2) + POP DE + INC B ; Make it SQR(2) + CALL DVBCDE ; Divide by SQR(2) + LD HL,UNITY ; Point to 1. + CALL SUBPHL ; Subtract FPREG from 1 + LD HL,LOGTAB ; Coefficient table + CALL SUMSER ; Evaluate sum of series + LD BC,8080H ; BCDE = -0.5 + LD DE,0000H + CALL FPADD ; Subtract 0.5 from FPREG + POP AF ; Restore scale factor + CALL RSCALE ; Re-scale number +MULLN2: LD BC,8031H ; BCDE = Ln(2) + LD DE,7218H + .BYTE 21H ; Skip "POP BC" and "POP DE" + +MULT: POP BC ; Get number from stack + POP DE +FPMULT: CALL TSTSGN ; Test sign of FPREG + RET Z ; Return zero if zero + LD L,0 ; Flag add exponents + CALL ADDEXP ; Add exponents + LD A,C ; Get MSB of multiplier + LD (MULVAL),A ; Save MSB of multiplier + EX DE,HL + LD (MULVAL+1),HL ; Save rest of multiplier + LD BC,0 ; Partial product (BCDE) = zero + LD D,B + LD E,B + LD HL,BNORM ; Address of normalise + PUSH HL ; Save for return + LD HL,MULT8 ; Address of 8 bit multiply + PUSH HL ; Save for NMSB,MSB + PUSH HL ; + LD HL,FPREG ; Point to number +MULT8: LD A,(HL) ; Get LSB of number + INC HL ; Point to NMSB + OR A ; Test LSB + JP Z,BYTSFT ; Zero - shift to next byte + PUSH HL ; Save address of number + LD L,8 ; 8 bits to multiply by +MUL8LP: RRA ; Shift LSB right + LD H,A ; Save LSB + LD A,C ; Get MSB + JP NC,NOMADD ; Bit was zero - Don't add + PUSH HL ; Save LSB and count + LD HL,(MULVAL+1) ; Get LSB and NMSB + ADD HL,DE ; Add NMSB and LSB + EX DE,HL ; Leave sum in DE + POP HL ; Restore MSB and count + LD A,(MULVAL) ; Get MSB of multiplier + ADC A,C ; Add MSB +NOMADD: RRA ; Shift MSB right + LD C,A ; Re-save MSB + LD A,D ; Get NMSB + RRA ; Shift NMSB right + LD D,A ; Re-save NMSB + LD A,E ; Get LSB + RRA ; Shift LSB right + LD E,A ; Re-save LSB + LD A,B ; Get VLSB + RRA ; Shift VLSB right + LD B,A ; Re-save VLSB + DEC L ; Count bits multiplied + LD A,H ; Get LSB of multiplier + JP NZ,MUL8LP ; More - Do it +POPHRT: POP HL ; Restore address of number + RET + +BYTSFT: LD B,E ; Shift partial product left + LD E,D + LD D,C + LD C,A + RET + +DIV10: CALL STAKFP ; Save FPREG on stack + LD BC,8420H ; BCDE = 10. + LD DE,0000H + CALL FPBCDE ; Move 10 to FPREG + +DIV: POP BC ; Get number from stack + POP DE +DVBCDE: CALL TSTSGN ; Test sign of FPREG + JP Z,DZERR ; Error if division by zero + LD L,-1 ; Flag subtract exponents + CALL ADDEXP ; Subtract exponents + INC (HL) ; Add 2 to exponent to adjust + INC (HL) + DEC HL ; Point to MSB + LD A,(HL) ; Get MSB of dividend + LD (DIV3),A ; Save for subtraction + DEC HL + LD A,(HL) ; Get NMSB of dividend + LD (DIV2),A ; Save for subtraction + DEC HL + LD A,(HL) ; Get MSB of dividend + LD (DIV1),A ; Save for subtraction + LD B,C ; Get MSB + EX DE,HL ; NMSB,LSB to HL + XOR A + LD C,A ; Clear MSB of quotient + LD D,A ; Clear NMSB of quotient + LD E,A ; Clear LSB of quotient + LD (DIV4),A ; Clear overflow count +DIVLP: PUSH HL ; Save divisor + PUSH BC + LD A,L ; Get LSB of number + CALL DIVSUP ; Subt' divisor from dividend + SBC A,0 ; Count for overflows + CCF + JP NC,RESDIV ; Restore divisor if borrow + LD (DIV4),A ; Re-save overflow count + POP AF ; Scrap divisor + POP AF + SCF ; Set carry to + .BYTE 0D2H ; Skip "POP BC" and "POP HL" + +RESDIV: POP BC ; Restore divisor + POP HL + LD A,C ; Get MSB of quotient + INC A + DEC A + RRA ; Bit 0 to bit 7 + JP M,RONDB ; Done - Normalise result + RLA ; Restore carry + LD A,E ; Get LSB of quotient + RLA ; Double it + LD E,A ; Put it back + LD A,D ; Get NMSB of quotient + RLA ; Double it + LD D,A ; Put it back + LD A,C ; Get MSB of quotient + RLA ; Double it + LD C,A ; Put it back + ADD HL,HL ; Double NMSB,LSB of divisor + LD A,B ; Get MSB of divisor + RLA ; Double it + LD B,A ; Put it back + LD A,(DIV4) ; Get VLSB of quotient + RLA ; Double it + LD (DIV4),A ; Put it back + LD A,C ; Get MSB of quotient + OR D ; Merge NMSB + OR E ; Merge LSB + JP NZ,DIVLP ; Not done - Keep dividing + PUSH HL ; Save divisor + LD HL,FPEXP ; Point to exponent + DEC (HL) ; Divide by 2 + POP HL ; Restore divisor + JP NZ,DIVLP ; Ok - Keep going + JP OVERR ; Overflow error + +ADDEXP: LD A,B ; Get exponent of dividend + OR A ; Test it + JP Z,OVTST3 ; Zero - Result zero + LD A,L ; Get add/subtract flag + LD HL,FPEXP ; Point to exponent + XOR (HL) ; Add or subtract it + ADD A,B ; Add the other exponent + LD B,A ; Save new exponent + RRA ; Test exponent for overflow + XOR B + LD A,B ; Get exponent + JP P,OVTST2 ; Positive - Test for overflow + ADD A,80H ; Add excess 128 + LD (HL),A ; Save new exponent + JP Z,POPHRT ; Zero - Result zero + CALL SIGNS ; Set MSBs and sign of result + LD (HL),A ; Save new exponent + DEC HL ; Point to MSB + RET + +OVTST1: CALL TSTSGN ; Test sign of FPREG + CPL ; Invert sign + POP HL ; Clean up stack +OVTST2: OR A ; Test if new exponent zero +OVTST3: POP HL ; Clear off return address + JP P,RESZER ; Result zero + JP OVERR ; Overflow error + +MLSP10: CALL BCDEFP ; Move FPREG to BCDE + LD A,B ; Get exponent + OR A ; Is it zero? + RET Z ; Yes - Result is zero + ADD A,2 ; Multiply by 4 + JP C,OVERR ; Overflow - ?OV Error + LD B,A ; Re-save exponent + CALL FPADD ; Add BCDE to FPREG (Times 5) + LD HL,FPEXP ; Point to exponent + INC (HL) ; Double number (Times 10) + RET NZ ; Ok - Return + JP OVERR ; Overflow error + +TSTSGN: LD A,(FPEXP) ; Get sign of FPREG + OR A + RET Z ; RETurn if number is zero + LD A,(FPREG+2) ; Get MSB of FPREG + .BYTE 0FEH ; Test sign +RETREL: CPL ; Invert sign + RLA ; Sign bit to carry +FLGDIF: SBC A,A ; Carry to all bits of A + RET NZ ; Return -1 if negative + INC A ; Bump to +1 + RET ; Positive - Return +1 + +SGN: CALL TSTSGN ; Test sign of FPREG +FLGREL: LD B,80H+8 ; 8 bit integer in exponent + LD DE,0 ; Zero NMSB and LSB +RETINT: LD HL,FPEXP ; Point to exponent + LD C,A ; CDE = MSB,NMSB and LSB + LD (HL),B ; Save exponent + LD B,0 ; CDE = integer to normalise + INC HL ; Point to sign of result + LD (HL),80H ; Set sign of result + RLA ; Carry = sign of integer + JP CONPOS ; Set sign of result + +ABS: CALL TSTSGN ; Test sign of FPREG + RET P ; Return if positive +INVSGN: LD HL,FPREG+2 ; Point to MSB + LD A,(HL) ; Get sign of mantissa + XOR 80H ; Invert sign of mantissa + LD (HL),A ; Re-save sign of mantissa + RET + +STAKFP: EX DE,HL ; Save code string address + LD HL,(FPREG) ; LSB,NLSB of FPREG + EX (SP),HL ; Stack them,get return + PUSH HL ; Re-save return + LD HL,(FPREG+2) ; MSB and exponent of FPREG + EX (SP),HL ; Stack them,get return + PUSH HL ; Re-save return + EX DE,HL ; Restore code string address + RET + +PHLTFP: CALL LOADFP ; Number at HL to BCDE +FPBCDE: EX DE,HL ; Save code string address + LD (FPREG),HL ; Save LSB,NLSB of number + LD H,B ; Exponent of number + LD L,C ; MSB of number + LD (FPREG+2),HL ; Save MSB and exponent + EX DE,HL ; Restore code string address + RET + +BCDEFP: LD HL,FPREG ; Point to FPREG +LOADFP: LD E,(HL) ; Get LSB of number + INC HL + LD D,(HL) ; Get NMSB of number + INC HL + LD C,(HL) ; Get MSB of number + INC HL + LD B,(HL) ; Get exponent of number +INCHL: INC HL ; Used for conditional "INC HL" + RET + +FPTHL: LD DE,FPREG ; Point to FPREG +DETHL4: LD B,4 ; 4 bytes to move +DETHLB: LD A,(DE) ; Get source + LD (HL),A ; Save destination + INC DE ; Next source + INC HL ; Next destination + DEC B ; Count bytes + JP NZ,DETHLB ; Loop if more + RET + +SIGNS: LD HL,FPREG+2 ; Point to MSB of FPREG + LD A,(HL) ; Get MSB + RLCA ; Old sign to carry + SCF ; Set MSBit + RRA ; Set MSBit of MSB + LD (HL),A ; Save new MSB + CCF ; Complement sign + RRA ; Old sign to carry + INC HL + INC HL + LD (HL),A ; Set sign of result + LD A,C ; Get MSB + RLCA ; Old sign to carry + SCF ; Set MSBit + RRA ; Set MSBit of MSB + LD C,A ; Save MSB + RRA + XOR (HL) ; New sign of result + RET + +CMPNUM: LD A,B ; Get exponent of number + OR A + JP Z,TSTSGN ; Zero - Test sign of FPREG + LD HL,RETREL ; Return relation routine + PUSH HL ; Save for return + CALL TSTSGN ; Test sign of FPREG + LD A,C ; Get MSB of number + RET Z ; FPREG zero - Number's MSB + LD HL,FPREG+2 ; MSB of FPREG + XOR (HL) ; Combine signs + LD A,C ; Get MSB of number + RET M ; Exit if signs different + CALL CMPFP ; Compare FP numbers + RRA ; Get carry to sign + XOR C ; Combine with MSB of number + RET + +CMPFP: INC HL ; Point to exponent + LD A,B ; Get exponent + CP (HL) ; Compare exponents + RET NZ ; Different + DEC HL ; Point to MBS + LD A,C ; Get MSB + CP (HL) ; Compare MSBs + RET NZ ; Different + DEC HL ; Point to NMSB + LD A,D ; Get NMSB + CP (HL) ; Compare NMSBs + RET NZ ; Different + DEC HL ; Point to LSB + LD A,E ; Get LSB + SUB (HL) ; Compare LSBs + RET NZ ; Different + POP HL ; Drop RETurn + POP HL ; Drop another RETurn + RET + +FPINT: LD B,A ; <- Move + LD C,A ; <- exponent + LD D,A ; <- to all + LD E,A ; <- bits + OR A ; Test exponent + RET Z ; Zero - Return zero + PUSH HL ; Save pointer to number + CALL BCDEFP ; Move FPREG to BCDE + CALL SIGNS ; Set MSBs & sign of result + XOR (HL) ; Combine with sign of FPREG + LD H,A ; Save combined signs + CALL M,DCBCDE ; Negative - Decrement BCDE + LD A,80H+24 ; 24 bits + SUB B ; Bits to shift + CALL SCALE ; Shift BCDE + LD A,H ; Get combined sign + RLA ; Sign to carry + CALL C,FPROND ; Negative - Round number up + LD B,0 ; Zero exponent + CALL C,COMPL ; If negative make positive + POP HL ; Restore pointer to number + RET + +DCBCDE: DEC DE ; Decrement BCDE + LD A,D ; Test LSBs + AND E + INC A + RET NZ ; Exit if LSBs not FFFF + DEC BC ; Decrement MSBs + RET + +INT: LD HL,FPEXP ; Point to exponent + LD A,(HL) ; Get exponent + CP 80H+24 ; Integer accuracy only? + LD A,(FPREG) ; Get LSB + RET NC ; Yes - Already integer + LD A,(HL) ; Get exponent + CALL FPINT ; F.P to integer + LD (HL),80H+24 ; Save 24 bit integer + LD A,E ; Get LSB of number + PUSH AF ; Save LSB + LD A,C ; Get MSB of number + RLA ; Sign to carry + CALL CONPOS ; Set sign of result + POP AF ; Restore LSB of number + RET + +MLDEBC: LD HL,0 ; Clear partial product + LD A,B ; Test multiplier + OR C + RET Z ; Return zero if zero + LD A,16 ; 16 bits +MLDBLP: ADD HL,HL ; Shift P.P left + JP C,BSERR ; ?BS Error if overflow + EX DE,HL + ADD HL,HL ; Shift multiplier left + EX DE,HL + JP NC,NOMLAD ; Bit was zero - No add + ADD HL,BC ; Add multiplicand + JP C,BSERR ; ?BS Error if overflow +NOMLAD: DEC A ; Count bits + JP NZ,MLDBLP ; More + RET + +ASCTFP: CP '-' ; Negative? + PUSH AF ; Save it and flags + JP Z,CNVNUM ; Yes - Convert number + CP '+' ; Positive? + JP Z,CNVNUM ; Yes - Convert number + DEC HL ; DEC 'cos GETCHR INCs +CNVNUM: CALL RESZER ; Set result to zero + LD B,A ; Digits after point counter + LD D,A ; Sign of exponent + LD E,A ; Exponent of ten + CPL + LD C,A ; Before or after point flag +MANLP: CALL GETCHR ; Get next character + JP C,ADDIG ; Digit - Add to number + CP '.' + JP Z,DPOINT ; '.' - Flag point + CP 'E' + JP NZ,CONEXP ; Not 'E' - Scale number + CALL GETCHR ; Get next character + CALL SGNEXP ; Get sign of exponent +EXPLP: CALL GETCHR ; Get next character + JP C,EDIGIT ; Digit - Add to exponent + INC D ; Is sign negative? + JP NZ,CONEXP ; No - Scale number + XOR A + SUB E ; Negate exponent + LD E,A ; And re-save it + INC C ; Flag end of number +DPOINT: INC C ; Flag point passed + JP Z,MANLP ; Zero - Get another digit +CONEXP: PUSH HL ; Save code string address + LD A,E ; Get exponent + SUB B ; Subtract digits after point +SCALMI: CALL P,SCALPL ; Positive - Multiply number + JP P,ENDCON ; Positive - All done + PUSH AF ; Save number of times to /10 + CALL DIV10 ; Divide by 10 + POP AF ; Restore count + INC A ; Count divides + +ENDCON: JP NZ,SCALMI ; More to do + POP DE ; Restore code string address + POP AF ; Restore sign of number + CALL Z,INVSGN ; Negative - Negate number + EX DE,HL ; Code string address to HL + RET + +SCALPL: RET Z ; Exit if no scaling needed +MULTEN: PUSH AF ; Save count + CALL MLSP10 ; Multiply number by 10 + POP AF ; Restore count + DEC A ; Count multiplies + RET + +ADDIG: PUSH DE ; Save sign of exponent + LD D,A ; Save digit + LD A,B ; Get digits after point + ADC A,C ; Add one if after point + LD B,A ; Re-save counter + PUSH BC ; Save point flags + PUSH HL ; Save code string address + PUSH DE ; Save digit + CALL MLSP10 ; Multiply number by 10 + POP AF ; Restore digit + SUB '0' ; Make it absolute + CALL RSCALE ; Re-scale number + POP HL ; Restore code string address + POP BC ; Restore point flags + POP DE ; Restore sign of exponent + JP MANLP ; Get another digit + +RSCALE: CALL STAKFP ; Put number on stack + CALL FLGREL ; Digit to add to FPREG +PADD: POP BC ; Restore number + POP DE + JP FPADD ; Add BCDE to FPREG and return + +EDIGIT: LD A,E ; Get digit + RLCA ; Times 2 + RLCA ; Times 4 + ADD A,E ; Times 5 + RLCA ; Times 10 + ADD A,(HL) ; Add next digit + SUB '0' ; Make it absolute + LD E,A ; Save new digit + JP EXPLP ; Look for another digit + +LINEIN: PUSH HL ; Save code string address + LD HL,INMSG ; Output " in " + CALL PRS ; Output string at HL + POP HL ; Restore code string address +PRNTHL: EX DE,HL ; Code string address to DE + XOR A + LD B,80H+24 ; 24 bits + CALL RETINT ; Return the integer + LD HL,PRNUMS ; Print number string + PUSH HL ; Save for return +NUMASC: LD HL,PBUFF ; Convert number to ASCII + PUSH HL ; Save for return + CALL TSTSGN ; Test sign of FPREG + LD (HL),' ' ; Space at start + JP P,SPCFST ; Positive - Space to start + LD (HL),'-' ; '-' sign at start +SPCFST: INC HL ; First byte of number + LD (HL),'0' ; '0' if zero + JP Z,JSTZER ; Return '0' if zero + PUSH HL ; Save buffer address + CALL M,INVSGN ; Negate FPREG if negative + XOR A ; Zero A + PUSH AF ; Save it + CALL RNGTST ; Test number is in range +SIXDIG: LD BC,9143H ; BCDE - 99999.9 + LD DE,4FF8H + CALL CMPNUM ; Compare numbers + OR A + JP PO,INRNG ; > 99999.9 - Sort it out + POP AF ; Restore count + CALL MULTEN ; Multiply by ten + PUSH AF ; Re-save count + JP SIXDIG ; Test it again + +GTSIXD: CALL DIV10 ; Divide by 10 + POP AF ; Get count + INC A ; Count divides + PUSH AF ; Re-save count + CALL RNGTST ; Test number is in range +INRNG: CALL ROUND ; Add 0.5 to FPREG + INC A + CALL FPINT ; F.P to integer + CALL FPBCDE ; Move BCDE to FPREG + LD BC,0306H ; 1E+06 to 1E-03 range + POP AF ; Restore count + ADD A,C ; 6 digits before point + INC A ; Add one + JP M,MAKNUM ; Do it in 'E' form if < 1E-02 + CP 6+1+1 ; More than 999999 ? + JP NC,MAKNUM ; Yes - Do it in 'E' form + INC A ; Adjust for exponent + LD B,A ; Exponent of number + LD A,2 ; Make it zero after + +MAKNUM: DEC A ; Adjust for digits to do + DEC A + POP HL ; Restore buffer address + PUSH AF ; Save count + LD DE,POWERS ; Powers of ten + DEC B ; Count digits before point + JP NZ,DIGTXT ; Not zero - Do number + LD (HL),'.' ; Save point + INC HL ; Move on + LD (HL),'0' ; Save zero + INC HL ; Move on +DIGTXT: DEC B ; Count digits before point + LD (HL),'.' ; Save point in case + CALL Z,INCHL ; Last digit - move on + PUSH BC ; Save digits before point + PUSH HL ; Save buffer address + PUSH DE ; Save powers of ten + CALL BCDEFP ; Move FPREG to BCDE + POP HL ; Powers of ten table + LD B, '0'-1 ; ASCII '0' - 1 +TRYAGN: INC B ; Count subtractions + LD A,E ; Get LSB + SUB (HL) ; Subtract LSB + LD E,A ; Save LSB + INC HL + LD A,D ; Get NMSB + SBC A,(HL) ; Subtract NMSB + LD D,A ; Save NMSB + INC HL + LD A,C ; Get MSB + SBC A,(HL) ; Subtract MSB + LD C,A ; Save MSB + DEC HL ; Point back to start + DEC HL + JP NC,TRYAGN ; No overflow - Try again + CALL PLUCDE ; Restore number + INC HL ; Start of next number + CALL FPBCDE ; Move BCDE to FPREG + EX DE,HL ; Save point in table + POP HL ; Restore buffer address + LD (HL),B ; Save digit in buffer + INC HL ; And move on + POP BC ; Restore digit count + DEC C ; Count digits + JP NZ,DIGTXT ; More - Do them + DEC B ; Any decimal part? + JP Z,DOEBIT ; No - Do 'E' bit +SUPTLZ: DEC HL ; Move back through buffer + LD A,(HL) ; Get character + CP '0' ; '0' character? + JP Z,SUPTLZ ; Yes - Look back for more + CP '.' ; A decimal point? + CALL NZ,INCHL ; Move back over digit + +DOEBIT: POP AF ; Get 'E' flag + JP Z,NOENED ; No 'E' needed - End buffer + LD (HL),'E' ; Put 'E' in buffer + INC HL ; And move on + LD (HL),'+' ; Put '+' in buffer + JP P,OUTEXP ; Positive - Output exponent + LD (HL),'-' ; Put '-' in buffer + CPL ; Negate exponent + INC A +OUTEXP: LD B,'0'-1 ; ASCII '0' - 1 +EXPTEN: INC B ; Count subtractions + SUB 10 ; Tens digit + JP NC,EXPTEN ; More to do + ADD A,'0'+10 ; Restore and make ASCII + INC HL ; Move on + LD (HL),B ; Save MSB of exponent +JSTZER: INC HL ; + LD (HL),A ; Save LSB of exponent + INC HL +NOENED: LD (HL),C ; Mark end of buffer + POP HL ; Restore code string address + RET + +RNGTST: LD BC,9474H ; BCDE = 999999. + LD DE,23F7H + CALL CMPNUM ; Compare numbers + OR A + POP HL ; Return address to HL + JP PO,GTSIXD ; Too big - Divide by ten + JP (HL) ; Otherwise return to caller + +HALF: .BYTE 00H,00H,00H,80H ; 0.5 + +POWERS: .BYTE 0A0H,086H,001H ; 100000 + .BYTE 010H,027H,000H ; 10000 + .BYTE 0E8H,003H,000H ; 1000 + .BYTE 064H,000H,000H ; 100 + .BYTE 00AH,000H,000H ; 10 + .BYTE 001H,000H,000H ; 1 + +NEGAFT: LD HL,INVSGN ; Negate result + EX (SP),HL ; To be done after caller + JP (HL) ; Return to caller + +SQR: CALL STAKFP ; Put value on stack + LD HL,HALF ; Set power to 1/2 + CALL PHLTFP ; Move 1/2 to FPREG + +POWER: POP BC ; Get base + POP DE + CALL TSTSGN ; Test sign of power + LD A,B ; Get exponent of base + JP Z,EXP ; Make result 1 if zero + JP P,POWER1 ; Positive base - Ok + OR A ; Zero to negative power? + JP Z,DZERR ; Yes - ?/0 Error +POWER1: OR A ; Base zero? + JP Z,SAVEXP ; Yes - Return zero + PUSH DE ; Save base + PUSH BC + LD A,C ; Get MSB of base + OR 01111111B ; Get sign status + CALL BCDEFP ; Move power to BCDE + JP P,POWER2 ; Positive base - Ok + PUSH DE ; Save power + PUSH BC + CALL INT ; Get integer of power + POP BC ; Restore power + POP DE + PUSH AF ; MSB of base + CALL CMPNUM ; Power an integer? + POP HL ; Restore MSB of base + LD A,H ; but don't affect flags + RRA ; Exponent odd or even? +POWER2: POP HL ; Restore MSB and exponent + LD (FPREG+2),HL ; Save base in FPREG + POP HL ; LSBs of base + LD (FPREG),HL ; Save in FPREG + CALL C,NEGAFT ; Odd power - Negate result + CALL Z,INVSGN ; Negative base - Negate it + PUSH DE ; Save power + PUSH BC + CALL LOG ; Get LOG of base + POP BC ; Restore power + POP DE + CALL FPMULT ; Multiply LOG by power + +EXP: CALL STAKFP ; Put value on stack + LD BC,08138H ; BCDE = 1/Ln(2) + LD DE,0AA3BH + CALL FPMULT ; Multiply value by 1/LN(2) + LD A,(FPEXP) ; Get exponent + CP 80H+8 ; Is it in range? + JP NC,OVTST1 ; No - Test for overflow + CALL INT ; Get INT of FPREG + ADD A,80H ; For excess 128 + ADD A,2 ; Exponent > 126? + JP C,OVTST1 ; Yes - Test for overflow + PUSH AF ; Save scaling factor + LD HL,UNITY ; Point to 1. + CALL ADDPHL ; Add 1 to FPREG + CALL MULLN2 ; Multiply by LN(2) + POP AF ; Restore scaling factor + POP BC ; Restore exponent + POP DE + PUSH AF ; Save scaling factor + CALL SUBCDE ; Subtract exponent from FPREG + CALL INVSGN ; Negate result + LD HL,EXPTAB ; Coefficient table + CALL SMSER1 ; Sum the series + LD DE,0 ; Zero LSBs + POP BC ; Scaling factor + LD C,D ; Zero MSB + JP FPMULT ; Scale result to correct value + +EXPTAB: .BYTE 8 ; Table used by EXP + .BYTE 040H,02EH,094H,074H ; -1/7! (-1/5040) + .BYTE 070H,04FH,02EH,077H ; 1/6! ( 1/720) + .BYTE 06EH,002H,088H,07AH ; -1/5! (-1/120) + .BYTE 0E6H,0A0H,02AH,07CH ; 1/4! ( 1/24) + .BYTE 050H,0AAH,0AAH,07EH ; -1/3! (-1/6) + .BYTE 0FFH,0FFH,07FH,07FH ; 1/2! ( 1/2) + .BYTE 000H,000H,080H,081H ; -1/1! (-1/1) + .BYTE 000H,000H,000H,081H ; 1/0! ( 1/1) + +SUMSER: CALL STAKFP ; Put FPREG on stack + LD DE,MULT ; Multiply by "X" + PUSH DE ; To be done after + PUSH HL ; Save address of table + CALL BCDEFP ; Move FPREG to BCDE + CALL FPMULT ; Square the value + POP HL ; Restore address of table +SMSER1: CALL STAKFP ; Put value on stack + LD A,(HL) ; Get number of coefficients + INC HL ; Point to start of table + CALL PHLTFP ; Move coefficient to FPREG + .BYTE 06H ; Skip "POP AF" +SUMLP: POP AF ; Restore count + POP BC ; Restore number + POP DE + DEC A ; Cont coefficients + RET Z ; All done + PUSH DE ; Save number + PUSH BC + PUSH AF ; Save count + PUSH HL ; Save address in table + CALL FPMULT ; Multiply FPREG by BCDE + POP HL ; Restore address in table + CALL LOADFP ; Number at HL to BCDE + PUSH HL ; Save address in table + CALL FPADD ; Add coefficient to FPREG + POP HL ; Restore address in table + JP SUMLP ; More coefficients + +RND: CALL TSTSGN ; Test sign of FPREG + LD HL,SEED+2 ; Random number seed + JP M,RESEED ; Negative - Re-seed + LD HL,LSTRND ; Last random number + CALL PHLTFP ; Move last RND to FPREG + LD HL,SEED+2 ; Random number seed + RET Z ; Return if RND(0) + ADD A,(HL) ; Add (SEED)+2) + AND 00000111B ; 0 to 7 + LD B,0 + LD (HL),A ; Re-save seed + INC HL ; Move to coefficient table + ADD A,A ; 4 bytes + ADD A,A ; per entry + LD C,A ; BC = Offset into table + ADD HL,BC ; Point to coefficient + CALL LOADFP ; Coefficient to BCDE + CALL FPMULT ; ; Multiply FPREG by coefficient + LD A,(SEED+1) ; Get (SEED+1) + INC A ; Add 1 + AND 00000011B ; 0 to 3 + LD B,0 + CP 1 ; Is it zero? + ADC A,B ; Yes - Make it 1 + LD (SEED+1),A ; Re-save seed + LD HL,RNDTAB-4 ; Addition table + ADD A,A ; 4 bytes + ADD A,A ; per entry + LD C,A ; BC = Offset into table + ADD HL,BC ; Point to value + CALL ADDPHL ; Add value to FPREG +RND1: CALL BCDEFP ; Move FPREG to BCDE + LD A,E ; Get LSB + LD E,C ; LSB = MSB + XOR 01001111B ; Fiddle around + LD C,A ; New MSB + LD (HL),80H ; Set exponent + DEC HL ; Point to MSB + LD B,(HL) ; Get MSB + LD (HL),80H ; Make value -0.5 + LD HL,SEED ; Random number seed + INC (HL) ; Count seed + LD A,(HL) ; Get seed + SUB 171 ; Do it modulo 171 + JP NZ,RND2 ; Non-zero - Ok + LD (HL),A ; Zero seed + INC C ; Fillde about + DEC D ; with the + INC E ; number +RND2: CALL BNORM ; Normalise number + LD HL,LSTRND ; Save random number + JP FPTHL ; Move FPREG to last and return + +RESEED: LD (HL),A ; Re-seed random numbers + DEC HL + LD (HL),A + DEC HL + LD (HL),A + JP RND1 ; Return RND seed + +RNDTAB: .BYTE 068H,0B1H,046H,068H ; Table used by RND + .BYTE 099H,0E9H,092H,069H + .BYTE 010H,0D1H,075H,068H + +COS: LD HL,HALFPI ; Point to PI/2 + CALL ADDPHL ; Add it to PPREG +SIN: CALL STAKFP ; Put angle on stack + LD BC,8349H ; BCDE = 2 PI + LD DE,0FDBH + CALL FPBCDE ; Move 2 PI to FPREG + POP BC ; Restore angle + POP DE + CALL DVBCDE ; Divide angle by 2 PI + CALL STAKFP ; Put it on stack + CALL INT ; Get INT of result + POP BC ; Restore number + POP DE + CALL SUBCDE ; Make it 0 <= value < 1 + LD HL,QUARTR ; Point to 0.25 + CALL SUBPHL ; Subtract value from 0.25 + CALL TSTSGN ; Test sign of value + SCF ; Flag positive + JP P,SIN1 ; Positive - Ok + CALL ROUND ; Add 0.5 to value + CALL TSTSGN ; Test sign of value + OR A ; Flag negative +SIN1: PUSH AF ; Save sign + CALL P,INVSGN ; Negate value if positive + LD HL,QUARTR ; Point to 0.25 + CALL ADDPHL ; Add 0.25 to value + POP AF ; Restore sign + CALL NC,INVSGN ; Negative - Make positive + LD HL,SINTAB ; Coefficient table + JP SUMSER ; Evaluate sum of series + +HALFPI: .BYTE 0DBH,00FH,049H,081H ; 1.5708 (PI/2) + +QUARTR: .BYTE 000H,000H,000H,07FH ; 0.25 + +SINTAB: .BYTE 5 ; Table used by SIN + .BYTE 0BAH,0D7H,01EH,086H ; 39.711 + .BYTE 064H,026H,099H,087H ;-76.575 + .BYTE 058H,034H,023H,087H ; 81.602 + .BYTE 0E0H,05DH,0A5H,086H ;-41.342 + .BYTE 0DAH,00FH,049H,083H ; 6.2832 + +TAN: CALL STAKFP ; Put angle on stack + CALL SIN ; Get SIN of angle + POP BC ; Restore angle + POP HL + CALL STAKFP ; Save SIN of angle + EX DE,HL ; BCDE = Angle + CALL FPBCDE ; Angle to FPREG + CALL COS ; Get COS of angle + JP DIV ; TAN = SIN / COS + +ATN: CALL TSTSGN ; Test sign of value + CALL M,NEGAFT ; Negate result after if -ve + CALL M,INVSGN ; Negate value if -ve + LD A,(FPEXP) ; Get exponent + CP 81H ; Number less than 1? + JP C,ATN1 ; Yes - Get arc tangnt + LD BC,8100H ; BCDE = 1 + LD D,C + LD E,C + CALL DVBCDE ; Get reciprocal of number + LD HL,SUBPHL ; Sub angle from PI/2 + PUSH HL ; Save for angle > 1 +ATN1: LD HL,ATNTAB ; Coefficient table + CALL SUMSER ; Evaluate sum of series + LD HL,HALFPI ; PI/2 - angle in case > 1 + RET ; Number > 1 - Sub from PI/2 + +ATNTAB: .BYTE 9 ; Table used by ATN + .BYTE 04AH,0D7H,03BH,078H ; 1/17 + .BYTE 002H,06EH,084H,07BH ;-1/15 + .BYTE 0FEH,0C1H,02FH,07CH ; 1/13 + .BYTE 074H,031H,09AH,07DH ;-1/11 + .BYTE 084H,03DH,05AH,07DH ; 1/9 + .BYTE 0C8H,07FH,091H,07EH ;-1/7 + .BYTE 0E4H,0BBH,04CH,07EH ; 1/5 + .BYTE 06CH,0AAH,0AAH,07FH ;-1/3 + .BYTE 000H,000H,000H,081H ; 1/1 + + +ARET: RET ; A RETurn instruction + +GETINP: RST 10H ;input a character + RET + +CLS: + LD A,CS ; ASCII Clear screen + JP MONOUT ; Output character + +WIDTH: CALL GETINT ; Get integer 0-255 + LD A,E ; Width to A + LD (LWIDTH),A ; Set width + RET + +LINES: CALL GETNUM ; Get a number + CALL DEINT ; Get integer -32768 to 32767 + LD (LINESC),DE ; Set lines counter + LD (LINESN),DE ; Set lines number + RET + +DEEK: CALL DEINT ; Get integer -32768 to 32767 + PUSH DE ; Save number + POP HL ; Number to HL + LD B,(HL) ; Get LSB of contents + INC HL + LD A,(HL) ; Get MSB of contents + JP ABPASS ; Return integer AB + +DOKE: CALL GETNUM ; Get a number + CALL DEINT ; Get integer -32768 to 32767 + PUSH DE ; Save address + CALL CHKSYN ; Make sure ',' follows + .BYTE ',' + CALL GETNUM ; Get a number + CALL DEINT ; Get integer -32768 to 32767 + EX (SP),HL ; Save value,get address + LD (HL),E ; Save LSB of value + INC HL + LD (HL),D ; Save MSB of value + POP HL ; Restore code string address + RET + + +; HEX$(nn) Convert 16 bit number to Hexadecimal string + +HEX: CALL TSTNUM ; Verify it's a number + CALL DEINT ; Get integer -32768 to 32767 + PUSH BC ; Save contents of BC + LD HL,PBUFF + LD A,D ; Get high order into A + CP $0 + JR Z,HEX2 ; Skip output if both high digits are zero + CALL BYT2ASC ; Convert D to ASCII + LD A,B + CP '0' + JR Z,HEX1 ; Don't store high digit if zero + LD (HL),B ; Store it to PBUFF + INC HL ; Next location +HEX1: LD (HL),C ; Store C to PBUFF+1 + INC HL ; Next location +HEX2: LD A,E ; Get lower byte + CALL BYT2ASC ; Convert E to ASCII + LD A,D + CP $0 + JR NZ,HEX3 ; If upper byte was not zero then always print lower byte + LD A,B + CP '0' ; If high digit of lower byte is zero then don't print + JR Z,HEX4 +HEX3: LD (HL),B ; to PBUFF+2 + INC HL ; Next location +HEX4: LD (HL),C ; to PBUFF+3 + INC HL ; PBUFF+4 to zero + XOR A ; Terminating character + LD (HL),A ; Store zero to terminate + INC HL ; Make sure PBUFF is terminated + LD (HL),A ; Store the double zero there + POP BC ; Get BC back + LD HL,PBUFF ; Reset to start of PBUFF + JP STR1 ; Convert the PBUFF to a string and return it + +BYT2ASC LD B,A ; Save original value + AND $0F ; Strip off upper nybble + CP $0A ; 0-9? + JR C,ADD30 ; If A-F, add 7 more + ADD A,$07 ; Bring value up to ASCII A-F +ADD30 ADD A,$30 ; And make ASCII + LD C,A ; Save converted char to C + LD A,B ; Retrieve original value + RRCA ; and Rotate it right + RRCA + RRCA + RRCA + AND $0F ; Mask off upper nybble + CP $0A ; 0-9? < A hex? + JR C,ADD301 ; Skip Add 7 + ADD A,$07 ; Bring it up to ASCII A-F +ADD301 ADD A,$30 ; And make it full ASCII + LD B,A ; Store high order byte + RET + +; Convert "&Hnnnn" to FPREG +; Gets a character from (HL) checks for Hexadecimal ASCII numbers "&Hnnnn" +; Char is in A, NC if char is ;<=>?@ A-z, CY is set if 0-9 +HEXTFP EX DE,HL ; Move code string pointer to DE + LD HL,$0000 ; Zero out the value + CALL GETHEX ; Check the number for valid hex + JP C,HXERR ; First value wasn't hex, HX error + JR HEXLP1 ; Convert first character +HEXLP CALL GETHEX ; Get second and addtional characters + JR C,HEXIT ; Exit if not a hex character +HEXLP1 ADD HL,HL ; Rotate 4 bits to the left + ADD HL,HL + ADD HL,HL + ADD HL,HL + OR L ; Add in D0-D3 into L + LD L,A ; Save new value + JR HEXLP ; And continue until all hex characters are in + +GETHEX INC DE ; Next location + LD A,(DE) ; Load character at pointer + CP ' ' + JP Z,GETHEX ; Skip spaces + SUB $30 ; Get absolute value + RET C ; < "0", error + CP $0A + JR C,NOSUB7 ; Is already in the range 0-9 + SUB $07 ; Reduce to A-F + CP $0A ; Value should be $0A-$0F at this point + RET C ; CY set if was : ; < = > ? @ +NOSUB7 CP $10 ; > Greater than "F"? + CCF + RET ; CY set if it wasn't valid hex + +HEXIT EX DE,HL ; Value into DE, Code string into HL + LD A,D ; Load DE into AC + LD C,E ; For prep to + PUSH HL + CALL ACPASS ; ACPASS to set AC as integer into FPREG + POP HL + RET + +HXERR: LD E,HX ; ?HEX Error + JP ERROR + +; BIN$(NN) Convert integer to a 1-16 char binary string +BIN: CALL TSTNUM ; Verify it's a number + CALL DEINT ; Get integer -32768 to 32767 +BIN2: PUSH BC ; Save contents of BC + LD HL,PBUFF + LD B,17 ; One higher than max char count +ZEROSUP: ; Suppress leading zeros + DEC B ; Max 16 chars + LD A,B + CP $01 + JR Z,BITOUT ; Always output at least one character + RL E + RL D + JR NC,ZEROSUP + JR BITOUT2 +BITOUT: + RL E + RL D ; Top bit now in carry +BITOUT2: + LD A,'0' ; Char for '0' + ADC A,0 ; If carry set then '0' --> '1' + LD (HL),A + INC HL + DEC B + JR NZ,BITOUT + XOR A ; Terminating character + LD (HL),A ; Store zero to terminate + INC HL ; Make sure PBUFF is terminated + LD (HL),A ; Store the double zero there + POP BC + LD HL,PBUFF + JP STR1 + +; Convert "&Bnnnn" to FPREG +; Gets a character from (HL) checks for Binary ASCII numbers "&Bnnnn" +BINTFP: EX DE,HL ; Move code string pointer to DE + LD HL,$0000 ; Zero out the value + CALL CHKBIN ; Check the number for valid bin + JP C,BINERR ; First value wasn't bin, HX error +BINIT: SUB '0' + ADD HL,HL ; Rotate HL left + OR L + LD L,A + CALL CHKBIN ; Get second and addtional characters + JR NC,BINIT ; Process if a bin character + EX DE,HL ; Value into DE, Code string into HL + LD A,D ; Load DE into AC + LD C,E ; For prep to + PUSH HL + CALL ACPASS ; ACPASS to set AC as integer into FPREG + POP HL + RET + +; Char is in A, NC if char is 0 or 1 +CHKBIN: INC DE + LD A,(DE) + CP ' ' + JP Z,CHKBIN ; Skip spaces + CP '0' ; Set C if < '0' + RET C + CP '2' + CCF ; Set C if > '1' + RET + +BINERR: LD E,BN ; ?BIN Error + JP ERROR + + +JJUMP1: + LD IX,-1 ; Flag cold start + JP CSTART ; Go and initialise + +MONOUT: + JP $0008 ; output a char + + +MONITR: + JP $0000 ; Restart (Normally Monitor Start) + + +INITST: LD A,0 ; Clear break flag + LD (BRKFLG),A + JP INIT + +ARETN: RETN ; Return from NMI + + +TSTBIT: PUSH AF ; Save bit mask + AND B ; Get common bits + POP BC ; Restore bit mask + CP B ; Same bit set? + LD A,0 ; Return 0 in A + RET + +OUTNCR: CALL OUTC ; Output character in A + JP PRNTCRLF ; Output CRLF + +.end + diff --git a/Z80 CPM and bootloader (basmon)/source/cbios128.asm b/Z80 CPM and bootloader (basmon)/source/cbios128.asm new file mode 100644 index 0000000..6c60444 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/cbios128.asm @@ -0,0 +1,927 @@ +;================================================================================== +; Contents of this file are copyright Grant Searle +; Blocking/unblocking routines are the published version by Digital Research +; (bugfixed, as found on the web) +; +; You have permission to use this for NON COMMERCIAL USE ONLY +; If you wish to use it elsewhere, please include an acknowledgement to myself. +; +; http://searle.hostei.com/grant/index.html +; +; eMail: home.micros01@btinternet.com +; +; If the above don't work, please perform an Internet search to see if I have +; updated the web page hosting service. +; +;================================================================================== + +ccp .EQU 0D000h ; Base of CCP. +bdos .EQU ccp + 0806h ; Base of BDOS. +bios .EQU ccp + 1600h ; Base of BIOS. + +; Set CP/M low memory datA, vector and buffer addresses. + +iobyte .EQU 03h ; Intel standard I/O definition byte. +userdrv .EQU 04h ; Current user number and drive. +tpabuf .EQU 80h ; Default I/O buffer and command line storage. + + +SD_DATA .EQU 088H +SD_CONTROL .EQU 089H +SD_STATUS .EQU 089H +SD_LBA0 .EQU 08AH +SD_LBA1 .EQU 08BH +SD_LBA2 .EQU 08CH + +RTS_HIGH .EQU 0D5H +RTS_LOW .EQU 095H + +ACIA0_D .EQU $81 +ACIA0_C .EQU $80 +ACIA1_D .EQU $83 +ACIA1_C .EQU $82 + +nmi .EQU 66H + +blksiz .equ 4096 ;CP/M allocation size +hstsiz .equ 512 ;host disk sector size +hstspt .equ 32 ;host disk sectors/trk +hstblk .equ hstsiz/128 ;CP/M sects/host buff +cpmspt .equ hstblk * hstspt ;CP/M sectors/track +secmsk .equ hstblk-1 ;sector mask + ;compute sector mask +;secshf .equ 2 ;log2(hstblk) + +wrall .equ 0 ;write to allocated +wrdir .equ 1 ;write to directory +wrual .equ 2 ;write to unallocated + +LF .EQU 0AH ;line feed +FF .EQU 0CH ;form feed +CR .EQU 0DH ;carriage RETurn + +;================================================================================================ + + .ORG bios ; BIOS origin. + +;================================================================================================ +; BIOS jump table. +;================================================================================================ + JP boot ; 0 Initialize. +wboote: JP wboot ; 1 Warm boot. + JP const ; 2 Console status. + JP conin ; 3 Console input. + JP conout ; 4 Console OUTput. + JP list ; 5 List OUTput. + JP punch ; 6 punch OUTput. + JP reader ; 7 Reader input. + JP home ; 8 Home disk. + JP seldsk ; 9 Select disk. + JP settrk ; 10 Select track. + JP setsec ; 11 Select sector. + JP setdma ; 12 Set DMA ADDress. + JP read ; 13 Read 128 bytes. + JP write ; 14 Write 128 bytes. + JP listst ; 15 List status. + JP sectran ; 16 Sector translate. + +;================================================================================================ +; Disk parameter headers for disk 0 to 15 +;================================================================================================ +dpbase: + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb0,0000h,alv00 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv01 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv02 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv03 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv04 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv05 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv06 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv07 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv08 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv09 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv10 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv11 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv12 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv13 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv14 + .DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv15 + +; First drive has a reserved track for CP/M +dpb0: + .DW 128 ;SPT - sectors per track + .DB 5 ;BSH - block shift factor + .DB 31 ;BLM - block mask + .DB 1 ;EXM - Extent mask + .DW 2043 ; (2047-4) DSM - Storage size (blocks - 1) + .DW 511 ;DRM - Number of directory entries - 1 + .DB 240 ;AL0 - 1 bit set per directory block + .DB 0 ;AL1 - " + .DW 0 ;CKS - DIR check vector size (DRM+1)/4 (0=fixed disk) + .DW 1 ;OFF - Reserved tracks + +dpb: + .DW 128 ;SPT - sectors per track + .DB 5 ;BSH - block shift factor + .DB 31 ;BLM - block mask + .DB 1 ;EXM - Extent mask + .DW 2047 ;DSM - Storage size (blocks - 1) + .DW 511 ;DRM - Number of directory entries - 1 + .DB 240 ;AL0 - 1 bit set per directory block + .DB 0 ;AL1 - " + .DW 0 ;CKS - DIR check vector size (DRM+1)/4 (0=fixed disk) + .DW 0 ;OFF - Reserved tracks + +;================================================================================================ +; Cold boot +;================================================================================================ + +boot: + DI ; Disable interrupts. + LD SP,biosstack ; Set default stack. + +; Turn off ROM + + LD A,$01 + OUT ($38),A + + LD A,RTS_LOW + OUT (ACIA0_C),A ; Initialise ACIA0 + OUT (ACIA1_C),A ; Initialise ACIA1 + + CALL printInline + .DB FF + .TEXT "CP/M BIOS 2.0 by G. Searle 2013" + .DB CR,LF + .DB CR,LF + .TEXT "CP/M 2.2 " + .TEXT "(c)" + .TEXT " 1979 by Digital Research" + .DB CR,LF,0 + + ; CALL sdPreamble?? + + XOR a ; Clear I/O & drive bytes. + LD (userdrv),A + JP gocpm + +;================================================================================================ +; Warm boot +;================================================================================================ + +wboot: + DI ; Disable interrupts. + LD SP,biosstack ; Set default stack. + + LD B,11 ; Number of sectors to reload + + LD A,0 + LD (hstsec),A + OUT (SD_LBA2),A + OUT (SD_LBA1),A + + LD HL,ccp + +wbRdAllSecs: + +wBrdWait1: IN A,(SD_STATUS) + CP 128 + JR NZ,wBrdWait1 + + LD A,(hstsec) + OUT (SD_LBA0),A + + LD A,$00 ; 00 = Read block + OUT (SD_CONTROL),A + PUSH BC + + LD c,4 +wBrd4secs: + LD b,128 +wBrdByte: + +wBrdWait2: IN A,(SD_STATUS) + CP 224 ; Read byte waiting + JR NZ,wBrdWait2 + + IN A,(SD_DATA) + + LD (HL),A + INC HL + dec b + JR NZ, wBrdByte + + dec c + JR NZ,wBrd4secs + + LD A,(hstsec) + INC A + LD (hstsec),A + + POP BC + + DJNZ wbRdAllSecs +;================================================================================================ +; Common code for cold and warm boot +;================================================================================================ + +gocpm: + xor a ;0 to accumulator + ld (hstact),a ;host buffer inactive + ld (unacnt),a ;clear unalloc count + + LD HL,tpabuf ; Address of BIOS DMA buffer. + LD (dmaAddr),HL + LD A,0C3h ; Opcode for 'JP'. + LD (00h),A ; Load at start of RAM. + LD HL,wboote ; Address of jump for a warm boot. + LD (01h),HL + LD (05h),A ; Opcode for 'JP'. + LD HL,bdos ; Address of jump for the BDOS. + LD (06h),HL + LD A,(userdrv) ; Save new drive number (0). + LD c,A ; Pass drive number in C. + + JP ccp ; Start CP/M by jumping to the CCP. + +;================================================================================================ +; Console I/O routines +;================================================================================================ + + +;------------------------------------------------------------------------------------------------ +const: + LD A,(iobyte) + AND 00001011b ; Mask off console and high bit of reader + CP 00001010b ; redirected to reader on UR1/2 (Serial A) + JR Z,constA + CP 00000010b ; redirected to reader on TTY/RDR (Serial B) + JR Z,constB + + AND $03 ; remove the reader from the mask - only console bits then remain + CP $01 + JR NZ,constB +constA: + IN A,(ACIA0_C) ; Status byte + AND $01 + CP $0 ; Z flag set if no char + JR Z, dataAEmpty + LD A,0FFH + RET +dataAEmpty: + LD A,0 + RET + + +constB: + IN A,(ACIA1_C) ; Status byte + AND $01 + CP $0 ; Z flag set if no char + JR Z, dataBEmpty + LD A,0FFH + RET +dataBEmpty: + LD A,0 + RET + +;------------------------------------------------------------------------------------------------ +reader: + PUSH AF +reader2: LD A,(iobyte) + AND $08 + CP $08 + JR NZ,coninB + JR coninA +;------------------------------------------------------------------------------------------------ +conin: + PUSH AF + LD A,(iobyte) + AND $03 + CP $02 + JR Z,reader2 ; "BAT:" redirect + CP $01 + JR NZ,coninB + + +coninA: + POP AF +waitForCharA: + IN A,(ACIA0_C) ; Status byte + AND $01 + CP $0 ; Z flag set if no char + JR Z, waitForCharA + IN A,(ACIA0_D) + + RET ; Char ready in A + + +coninB: + POP AF +waitForCharB: + IN A,(ACIA1_C) ; Status byte + AND $01 + CP $0 ; Z flag set if no char + JR Z, waitForCharB + IN A,(ACIA1_D) + + RET ; Char ready in A + +;------------------------------------------------------------------------------------------------ +list: PUSH AF ; Store character +list2: LD A,(iobyte) + AND $C0 + CP $40 + JR NZ,conoutB1 + JR conoutA1 + +;------------------------------------------------------------------------------------------------ +punch: PUSH AF ; Store character + LD A,(iobyte) + AND $20 + CP $20 + JR NZ,conoutB1 + JR conoutA1 + +;------------------------------------------------------------------------------------------------ +conout: PUSH AF + LD A,(iobyte) + AND $03 + CP $02 + JR Z,list2 ; "BAT:" redirect + CP $01 + JR NZ,conoutB1 + +conoutA1: CALL CKACIA0 ; See if ACIA channel A is finished transmitting + JR Z,conoutA1 ; Loop until ACIA flag signals ready + LD A,C + OUT (ACIA0_D),A ; OUTput the character + POP AF + RET + +conoutB1: CALL CKACIA1 ; See if ACIA channel B is finished transmitting + JR Z,conoutB1 ; Loop until ACIA flag signals ready + LD A,C + OUT (ACIA1_D),A ; OUTput the character + POP AF + RET + +;------------------------------------------------------------------------------------------------ +CKACIA0 + IN A,(ACIA0_C) ; Status byte D1=TX Buff Empty, D0=RX char ready + RRCA ; Rotates RX status into Carry Flag, + BIT 0,A ; Set Zero flag if still transmitting character + RET + +CKACIA1 + IN A,(ACIA1_C) ; Status byte D1=TX Buff Empty, D0=RX char ready + RRCA ; Rotates RX status into Carry Flag, + BIT 0,A ; Set Zero flag if still transmitting character + RET + +;------------------------------------------------------------------------------------------------ +listst: LD A,$FF ; Return list status of 0xFF (ready). + RET + +;================================================================================================ +; Disk processing entry points +;================================================================================================ + +seldsk: + LD HL,$0000 + LD A,C + CP 16 ; 16 for 128MB disk, 8 for 64MB disk + jr C,chgdsk ; if invalid drive will give BDOS error + LD A,(userdrv) ; so set the drive back to a: + CP C ; If the default disk is not the same as the + RET NZ ; selected drive then return, + XOR A ; else reset default back to a: + LD (userdrv),A ; otherwise will be stuck in a loop + LD (sekdsk),A + ret + +chgdsk: LD (sekdsk),A + RLC a ;*2 + RLC a ;*4 + RLC a ;*8 + RLC a ;*16 + LD HL,dpbase + LD b,0 + LD c,A + ADD HL,BC + + RET + +;------------------------------------------------------------------------------------------------ +home: + ld a,(hstwrt) ;check for pending write + or a + jr nz,homed + ld (hstact),a ;clear host active flag +homed: + LD BC,0000h + +;------------------------------------------------------------------------------------------------ +settrk: LD (sektrk),BC ; Set track passed from BDOS in register BC. + RET + +;------------------------------------------------------------------------------------------------ +setsec: LD (seksec),BC ; Set sector passed from BDOS in register BC. + RET + +;------------------------------------------------------------------------------------------------ +setdma: LD (dmaAddr),BC ; Set DMA ADDress given by registers BC. + RET + +;------------------------------------------------------------------------------------------------ +sectran: PUSH BC + POP HL + RET + +;------------------------------------------------------------------------------------------------ +read: + ;read the selected CP/M sector + xor a + ld (unacnt),a + ld a,1 + ld (readop),a ;read operation + ld (rsflag),a ;must read data + ld a,wrual + ld (wrtype),a ;treat as unalloc + jp rwoper ;to perform the read + + +;------------------------------------------------------------------------------------------------ +write: + ;write the selected CP/M sector + xor a ;0 to accumulator + ld (readop),a ;not a read operation + ld a,c ;write type in c + ld (wrtype),a + cp wrual ;write unallocated? + jr nz,chkuna ;check for unalloc +; +; write to unallocated, set parameters + ld a,blksiz/128 ;next unalloc recs + ld (unacnt),a + ld a,(sekdsk) ;disk to seek + ld (unadsk),a ;unadsk = sekdsk + ld hl,(sektrk) + ld (unatrk),hl ;unatrk = sectrk + ld a,(seksec) + ld (unasec),a ;unasec = seksec +; +chkuna: +; check for write to unallocated sector + ld a,(unacnt) ;any unalloc remain? + or a + jr z,alloc ;skip if not +; +; more unallocated records remain + dec a ;unacnt = unacnt-1 + ld (unacnt),a + ld a,(sekdsk) ;same disk? + ld hl,unadsk + cp (hl) ;sekdsk = unadsk? + jp nz,alloc ;skip if not +; +; disks are the same + ld hl,unatrk + call sektrkcmp ;sektrk = unatrk? + jp nz,alloc ;skip if not +; +; tracks are the same + ld a,(seksec) ;same sector? + ld hl,unasec + cp (hl) ;seksec = unasec? + jp nz,alloc ;skip if not +; +; match, move to next sector for future ref + inc (hl) ;unasec = unasec+1 + ld a,(hl) ;end of track? + cp cpmspt ;count CP/M sectors + jr c,noovf ;skip if no overflow +; +; overflow to next track + ld (hl),0 ;unasec = 0 + ld hl,(unatrk) + inc hl + ld (unatrk),hl ;unatrk = unatrk+1 +; +noovf: + ;match found, mark as unnecessary read + xor a ;0 to accumulator + ld (rsflag),a ;rsflag = 0 + jr rwoper ;to perform the write +; +alloc: + ;not an unallocated record, requires pre-read + xor a ;0 to accum + ld (unacnt),a ;unacnt = 0 + inc a ;1 to accum + ld (rsflag),a ;rsflag = 1 + +;------------------------------------------------------------------------------------------------ +rwoper: + ;enter here to perform the read/write + xor a ;zero to accum + ld (erflag),a ;no errors (yet) + ld a,(seksec) ;compute host sector + or a ;carry = 0 + rra ;shift right + or a ;carry = 0 + rra ;shift right + ld (sekhst),a ;host sector to seek +; +; active host sector? + ld hl,hstact ;host active flag + ld a,(hl) + ld (hl),1 ;always becomes 1 + or a ;was it already? + jr z,filhst ;fill host if not +; +; host buffer active, same as seek buffer? + ld a,(sekdsk) + ld hl,hstdsk ;same disk? + cp (hl) ;sekdsk = hstdsk? + jr nz,nomatch +; +; same disk, same track? + ld hl,hsttrk + call sektrkcmp ;sektrk = hsttrk? + jr nz,nomatch +; +; same disk, same track, same buffer? + ld a,(sekhst) + ld hl,hstsec ;sekhst = hstsec? + cp (hl) + jr z,match ;skip if match +; +nomatch: + ;proper disk, but not correct sector + ld a,(hstwrt) ;host written? + or a + call nz,writehst ;clear host buff +; +filhst: + ;may have to fill the host buffer + ld a,(sekdsk) + ld (hstdsk),a + ld hl,(sektrk) + ld (hsttrk),hl + ld a,(sekhst) + ld (hstsec),a + ld a,(rsflag) ;need to read? + or a + call nz,readhst ;yes, if 1 + xor a ;0 to accum + ld (hstwrt),a ;no pending write +; +match: + ;copy data to or from buffer + ld a,(seksec) ;mask buffer number + and secmsk ;least signif bits + ld l,a ;ready to shift + ld h,0 ;double count + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl +; hl has relative host buffer address + ld de,hstbuf + add hl,de ;hl = host address + ex de,hl ;now in DE + ld hl,(dmaAddr) ;get/put CP/M data + ld c,128 ;length of move + ld a,(readop) ;which way? + or a + jr nz,rwmove ;skip if read +; +; write operation, mark and switch direction + ld a,1 + ld (hstwrt),a ;hstwrt = 1 + ex de,hl ;source/dest swap +; +rwmove: + ;C initially 128, DE is source, HL is dest + ld a,(de) ;source character + inc de + ld (hl),a ;to dest + inc hl + dec c ;loop 128 times + jr nz,rwmove +; +; data has been moved to/from host buffer + ld a,(wrtype) ;write type + cp wrdir ;to directory? + ld a,(erflag) ;in case of errors + ret nz ;no further processing +; +; clear host buffer for directory write + or a ;errors? + ret nz ;skip if so + xor a ;0 to accum + ld (hstwrt),a ;buffer written + call writehst + ld a,(erflag) + ret + +;------------------------------------------------------------------------------------------------ +;Utility subroutine for 16-bit compare +sektrkcmp: + ;HL = .unatrk or .hsttrk, compare with sektrk + ex de,hl + ld hl,sektrk + ld a,(de) ;low byte compare + cp (HL) ;same? + ret nz ;return if not +; low bytes equal, test high 1s + inc de + inc hl + ld a,(de) + cp (hl) ;sets flags + ret + +;================================================================================================ +; Convert track/head/sector into LBA for physical access to the disk +;================================================================================================ +setLBAaddr: + LD HL,(hsttrk) + RLC L + RLC L + RLC L + RLC L + RLC L + LD A,L + AND 0E0H + LD L,A + LD A,(hstsec) + ADD A,L + LD (lba0),A + + LD HL,(hsttrk) + RRC L + RRC L + RRC L + LD A,L + AND 01FH + LD L,A + RLC H + RLC H + RLC H + RLC H + RLC H + LD A,H + AND 020H + LD H,A + LD A,(hstdsk) + RLC a + RLC a + RLC a + RLC a + RLC a + RLC a + AND 0C0H + ADD A,H + ADD A,L + LD (lba1),A + + LD A,(hstdsk) + RRC A + RRC A + AND 03H + LD (lba2),A + + LD a,00H + LD (lba3),A + + ; Transfer LBA to disk (LBA3 not used on SD card) + LD A,(lba2) + OUT (SD_LBA2),A + LD A,(lba1) + OUT (SD_LBA1),A + LD A,(lba0) + OUT (SD_LBA0),A + RET + +;================================================================================================ +; Read physical sector from host +;================================================================================================ + +readhst: + PUSH AF + PUSH BC + PUSH HL + +rdWait1: IN A,(SD_STATUS) + CP 128 + JR NZ,rdWait1 + + CALL setLBAaddr + + LD A,$00 ; 00 = Read block + OUT (SD_CONTROL),A + + LD c,4 + LD HL,hstbuf +rd4secs: + LD b,128 +rdByte: + +rdWait2: IN A,(SD_STATUS) + CP 224 ; Read byte waiting + JR NZ,rdWait2 + + IN A,(SD_DATA) + + LD (HL),A + INC HL + dec b + JR NZ, rdByte + dec c + JR NZ,rd4secs + + POP HL + POP BC + POP AF + + XOR a + ld (erflag),a + RET + + +;================================================================================================ +; Write physical sector to host +;================================================================================================ + +writehst: + PUSH AF + PUSH BC + PUSH HL + +wrWait1: IN A,(SD_STATUS) + CP 128 + JR NZ,wrWait1 + + CALL setLBAaddr + + LD A,$01 ; 01 = Write block + OUT (SD_CONTROL),A + + LD c,4 + LD HL,hstbuf +wr4secs: + LD b,128 +wrByte: + +wrWait2: IN A,(SD_STATUS) + CP 160 ; Write buffer empty + JR NZ,wrWait2 + + ; UPDATE S0urceror, inserted wait cycle between IN and OUT + ; to resolve unknown write issue in sd_controller.vhd in combination + ; with MISTer virtual SD interface sys/sd_card.sv + ; which results in hangs or write errors. + push bc + ld b,100 +_again: + djnz _again + pop bc + ; END UPDATE + + LD A,(HL) + OUT (SD_DATA),A + INC HL + dec b + JR NZ, wrByte + + dec c + JR NZ,wr4secs + + POP HL + POP BC + POP AF + + XOR a + ld (erflag),a + RET + +;================================================================================================ +; Utilities +;================================================================================================ + +printInline: + EX (SP),HL ; PUSH HL and put RET ADDress into HL + PUSH AF + PUSH BC +nextILChar: LD A,(HL) + CP 0 + JR Z,endOfPrint + LD C,A + CALL conout ; Print to TTY + iNC HL + JR nextILChar +endOfPrint: INC HL ; Get past "null" terminator + POP BC + POP AF + EX (SP),HL ; PUSH new RET ADDress on stack and restore HL + RET + +;================================================================================================ +; Data storage +;================================================================================================ + +dirbuf: .ds 128 ;scratch directory area +alv00: .ds 257 ;allocation vector 0 +alv01: .ds 257 ;allocation vector 1 +alv02: .ds 257 ;allocation vector 2 +alv03: .ds 257 ;allocation vector 3 +alv04: .ds 257 ;allocation vector 4 +alv05: .ds 257 ;allocation vector 5 +alv06: .ds 257 ;allocation vector 6 +alv07: .ds 257 ;allocation vector 7 +alv08: .ds 257 ;allocation vector 8 +alv09: .ds 257 ;allocation vector 9 +alv10: .ds 257 ;allocation vector 10 +alv11: .ds 257 ;allocation vector 11 +alv12: .ds 257 ;allocation vector 12 +alv13: .ds 257 ;allocation vector 13 +alv14: .ds 257 ;allocation vector 14 +alv15: .ds 257 ;allocation vector 15 + +lba0 .DB 00h +lba1 .DB 00h +lba2 .DB 00h +lba3 .DB 00h + + .DS 020h ; Start of BIOS stack area. +biosstack: .EQU $ + +sekdsk: .ds 1 ;seek disk number +sektrk: .ds 2 ;seek track number +seksec: .ds 2 ;seek sector number +; +hstdsk: .ds 1 ;host disk number +hsttrk: .ds 2 ;host track number +hstsec: .ds 1 ;host sector number +; +sekhst: .ds 1 ;seek shr secshf +hstact: .ds 1 ;host active flag +hstwrt: .ds 1 ;host written flag +; +unacnt: .ds 1 ;unalloc rec cnt +unadsk: .ds 1 ;last unalloc disk +unatrk: .ds 2 ;last unalloc track +unasec: .ds 1 ;last unalloc sector +; +erflag: .ds 1 ;error reporting +rsflag: .ds 1 ;read sector flag +readop: .ds 1 ;1 if read operation +wrtype: .ds 1 ;write operation type +dmaAddr: .ds 2 ;last dma address +hstbuf: .ds 512 ;host buffer + +hstBufEnd: .EQU $ + +biosEnd: .EQU $ + +; Disable the ROM, pop the active IO port from the stack (supplied by monitor), +; then start CP/M +popAndRun: + LD A,$01 + OUT ($38),A + + POP AF + CP $01 + JR Z,consoleAtB + LD A,$01 ;(List is TTY:, Punch is TTY:, Reader is TTY:, Console is CRT:) + JR setIOByte +consoleAtB: LD A,$00 ;(List is TTY:, Punch is TTY:, Reader is TTY:, Console is TTY:) +setIOByte: LD (iobyte),A + JP bios + + +;================================================================================= +; Relocate TPA area from 4100 to 0100 then start CP/M +; Used to manually transfer a loaded program after CP/M was previously loaded +;================================================================================= + + .org 0FFE8H + LD A,$01 + OUT ($38),A + + LD HL,04100H + LD DE,00100H + LD BC,08F00H + LDIR + JP bios + +;================================================================================= +; Normal start CP/M vector +;================================================================================= + + .ORG 0FFFEH + .dw popAndRun + + .END diff --git a/Z80 CPM and bootloader (basmon)/source/ch376s.bin b/Z80 CPM and bootloader (basmon)/source/ch376s.bin new file mode 100644 index 0000000..e4bc4c6 Binary files /dev/null and b/Z80 CPM and bootloader (basmon)/source/ch376s.bin differ diff --git a/Z80 CPM and bootloader (basmon)/source/ch376s_test.asm b/Z80 CPM and bootloader (basmon)/source/ch376s_test.asm new file mode 100644 index 0000000..17e5787 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/ch376s_test.asm @@ -0,0 +1,85 @@ +LF .EQU 0AH ;line feed +FF .EQU 0CH ;form feed +CR .EQU 0DH ;carriage RETurn +DOT .EQU '.' +CH375_CMD_CHECK_EXIST .EQU 06H +CH375_CMD_RESET_ALL .EQU 05H + + .ORG 4000H + + CALL printInline + .TEXT "Check CH376s communication" + .DB CR,LF,0 + + CALL printInline + .TEXT "Send A" + .DB CR,LF,0 + + ld a, CH375_CMD_RESET_ALL + out (20h),a + + ld a, CH375_CMD_CHECK_EXIST + out (20h),a + ld a, 'A' + out (20h),a + ; receive result + xor a + out (20h),a + in a, (20h) + xor 255 + + CALL printInline + .TEXT "Received " + .DB 0 + + RST 08H ; print contents of A + + CALL printInline + .DB CR,LF,0 + + ret + + ; LOOPBACK TEST + + ld b, 39h +outer: + ld a, b + cp 2fh + ret z + ; send out + out (20h),a +;inner: +; ld a, DOT +; rst 08h +; in a, (21h) +; bit 0,a +; jr z, inner + xor a + + ; read back + in a, (20h) + rst 08h ; should be 30h => 0..9 + + dec b + jr outer + + ret + +printInline: + EX (SP),HL ; PUSH HL and put RET ADDress into HL + PUSH AF + PUSH BC +nextILChar: LD A,(HL) + CP 0 + JR Z,endOfPrint + RST 08H + INC HL + JR nextILChar +endOfPrint: INC HL ; Get past "null" terminator + POP BC + POP AF + EX (SP),HL ; PUSH new RET ADDress on stack and restore HL + RET + + + .END diff --git a/Z80 CPM and bootloader (basmon)/source/cpm22.asm b/Z80 CPM and bootloader (basmon)/source/cpm22.asm new file mode 100644 index 0000000..cb88b05 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/cpm22.asm @@ -0,0 +1,3750 @@ +;************************************************************** +;* +;* C P / M version 2 . 2 +;* +;* Reconstructed from memory image on February 27, 1981 +;* +;* by Clark A. Calkins +;* +;************************************************************** +; +; Set memory limit here. This is the amount of contigeous +; ram starting from 0000. CP/M will reside at the end of this space. +; + +IOBYTE .EQU 3 ;i/o definition byte. +TDRIVE .EQU 4 ;current drive name and user number. +ENTRY .EQU 5 ;entry point for the cp/m bdos. +TFCB .EQU 5CH ;default file control block. +TBUFF .EQU 80H ;i/o buffer and command line storage. +TBASE .EQU 100H ;transiant program storage area. +; +; Set control character equates. +; +CNTRLC .EQU 3 ;control-c +CNTRLE .EQU 05H ;control-e +BS .EQU 08H ;backspace +TAB .EQU 09H ;tab +LF .EQU 0AH ;line feed +FF .EQU 0CH ;form feed +CR .EQU 0DH ;carriage return +CNTRLP .EQU 10H ;control-p +CNTRLR .EQU 12H ;control-r +CNTRLS .EQU 13H ;control-s +CNTRLU .EQU 15H ;control-u +CNTRLX .EQU 18H ;control-x +CNTRLZ .EQU 1AH ;control-z (end-of-file mark) +DEL .EQU 7FH ;rubout +; +; Set origin for CP/M +; + .ORG 0D000H +; +CBASE: JP COMMAND ;execute command processor (ccp). + JP CLEARBUF ;entry to empty input buffer before starting ccp. + +; +; Standard cp/m ccp input buffer. Format is (max length), +; (actual length), (char #1), (char #2), (char #3), etc. +; +INBUFF: .DB 127 ;length of input buffer. + .DB 0 ;current length of contents. + .TEXT "Copyright" + .TEXT " 1979 (c) by Digital Research " + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +INPOINT:.DW INBUFF+2 ;input line pointer +NAMEPNT:.DW 0 ;input line pointer used for error message. Points to +; ;start of name in error. +; +; Routine to print (A) on the console. All registers used. +; +PRINT: LD E,A ;setup bdos call. + LD C,2 + JP ENTRY +; +; Routine to print (A) on the console and to save (BC). +; +PRINTB: PUSH BC + CALL PRINT + POP BC + RET +; +; Routine to send a carriage return, line feed combination +; to the console. +; +CRLF: LD A,CR + CALL PRINTB + LD A,LF + JP PRINTB +; +; Routine to send one space to the console and save (BC). +; +SPACE: LD A,' ' + JP PRINTB +; +; Routine to print character string pointed to be (BC) on the +; console. It must terminate with a null byte. +; +PLINE: PUSH BC + CALL CRLF + POP HL +PLINE2: LD A,(HL) + OR A + RET Z + INC HL + PUSH HL + CALL PRINT + POP HL + JP PLINE2 +; +; Routine to reset the disk system. +; +RESDSK: LD C,13 + JP ENTRY +; +; Routine to select disk (A). +; +DSKSEL: LD E,A + LD C,14 + JP ENTRY +; +; Routine to call bdos and save the return code. The zero +; flag is set on a return of 0ffh. +; +ENTRY1: CALL ENTRY + LD (RTNCODE),A ;save return code. + INC A ;set zero if 0ffh returned. + RET +; +; Routine to open a file. (DE) must point to the FCB. +; +OPEN: LD C,15 + JP ENTRY1 +; +; Routine to open file at (FCB). +; +OPENFCB:XOR A ;clear the record number byte at fcb+32 + LD (FCB+32),A + LD DE,FCB + JP OPEN +; +; Routine to close a file. (DE) points to FCB. +; +CLOSE: LD C,16 + JP ENTRY1 +; +; Routine to search for the first file with ambigueous name +; (DE). +; +SRCHFST:LD C,17 + JP ENTRY1 +; +; Search for the next ambigeous file name. +; +SRCHNXT:LD C,18 + JP ENTRY1 +; +; Search for file at (FCB). +; +SRCHFCB:LD DE,FCB + JP SRCHFST +; +; Routine to delete a file pointed to by (DE). +; +DELETE: LD C,19 + JP ENTRY +; +; Routine to call the bdos and set the zero flag if a zero +; status is returned. +; +ENTRY2: CALL ENTRY + OR A ;set zero flag if appropriate. + RET +; +; Routine to read the next record from a sequential file. +; (DE) points to the FCB. +; +RDREC: LD C,20 + JP ENTRY2 +; +; Routine to read file at (FCB). +; +READFCB:LD DE,FCB + JP RDREC +; +; Routine to write the next record of a sequential file. +; (DE) points to the FCB. +; +WRTREC: LD C,21 + JP ENTRY2 +; +; Routine to create the file pointed to by (DE). +; +CREATE: LD C,22 + JP ENTRY1 +; +; Routine to rename the file pointed to by (DE). Note that +; the new name starts at (DE+16). +; +RENAM: LD C,23 + JP ENTRY +; +; Get the current user code. +; +GETUSR: LD E,0FFH +; +; Routne to get or set the current user code. +; If (E) is FF then this is a GET, else it is a SET. +; +GETSETUC: LD C,32 + JP ENTRY +; +; Routine to set the current drive byte at (TDRIVE). +; +SETCDRV:CALL GETUSR ;get user number + ADD A,A ;and shift into the upper 4 bits. + ADD A,A + ADD A,A + ADD A,A + LD HL,CDRIVE ;now add in the current drive number. + OR (HL) + LD (TDRIVE),A ;and save. + RET +; +; Move currently active drive down to (TDRIVE). +; +MOVECD: LD A,(CDRIVE) + LD (TDRIVE),A + RET +; +; Routine to convert (A) into upper case ascii. Only letters +; are affected. +; +UPPER: CP 'a' ;check for letters in the range of 'a' to 'z'. + RET C + CP '{' + RET NC + AND 5FH ;convert it if found. + RET +; +; Routine to get a line of input. We must check to see if the +; user is in (BATCH) mode. If so, then read the input from file +; ($$$.SUB). At the end, reset to console input. +; +GETINP: LD A,(BATCH) ;if =0, then use console input. + OR A + JP Z,GETINP1 +; +; Use the submit file ($$$.sub) which is prepared by a +; SUBMIT run. It must be on drive (A) and it will be deleted +; if and error occures (like eof). +; + LD A,(CDRIVE) ;select drive 0 if need be. + OR A + LD A,0 ;always use drive A for submit. + CALL NZ,DSKSEL ;select it if required. + LD DE,BATCHFCB + CALL OPEN ;look for it. + JP Z,GETINP1 ;if not there, use normal input. + LD A,(BATCHFCB+15) ;get last record number+1. + DEC A + LD (BATCHFCB+32),A + LD DE,BATCHFCB + CALL RDREC ;read last record. + JP NZ,GETINP1 ;quit on end of file. +; +; Move this record into input buffer. +; + LD DE,INBUFF+1 + LD HL,TBUFF ;data was read into buffer here. + LD B,128 ;all 128 characters may be used. + CALL HL2DE ;(HL) to (DE), (B) bytes. + LD HL,BATCHFCB+14 + LD (HL),0 ;zero out the 's2' byte. + INC HL ;and decrement the record count. + DEC (HL) + LD DE,BATCHFCB ;close the batch file now. + CALL CLOSE + JP Z,GETINP1 ;quit on an error. + LD A,(CDRIVE) ;re-select previous drive if need be. + OR A + CALL NZ,DSKSEL ;don't do needless selects. +; +; Print line just read on console. +; + LD HL,INBUFF+2 + CALL PLINE2 + CALL CHKCON ;check console, quit on a key. + JP Z,GETINP2 ;jump if no key is pressed. +; +; Terminate the submit job on any keyboard input. Delete this +; file such that it is not re-started and jump to normal keyboard +; input section. +; + CALL DELBATCH ;delete the batch file. + JP CMMND1 ;and restart command input. +; +; Get here for normal keyboard input. Delete the submit file +; incase there was one. +; +GETINP1:CALL DELBATCH ;delete file ($$$.sub). + CALL SETCDRV ;reset active disk. + LD C,10 ;get line from console device. + LD DE,INBUFF + CALL ENTRY + CALL MOVECD ;reset current drive (again). +; +; Convert input line to upper case. +; +GETINP2:LD HL,INBUFF+1 + LD B,(HL) ;(B)=character counter. +GETINP3:INC HL + LD A,B ;end of the line? + OR A + JP Z,GETINP4 + LD A,(HL) ;convert to upper case. + CALL UPPER + LD (HL),A + DEC B ;adjust character count. + JP GETINP3 +GETINP4:LD (HL),A ;add trailing null. + LD HL,INBUFF+2 + LD (INPOINT),HL ;reset input line pointer. + RET +; +; Routine to check the console for a key pressed. The zero +; flag is set is none, else the character is returned in (A). +; +CHKCON: LD C,11 ;check console. + CALL ENTRY + OR A + RET Z ;return if nothing. + LD C,1 ;else get character. + CALL ENTRY + OR A ;clear zero flag and return. + RET +; +; Routine to get the currently active drive number. +; +GETDSK: LD C,25 + JP ENTRY +; +; Set the stabdard dma address. +; +STDDMA: LD DE,TBUFF +; +; Routine to set the dma address to (DE). +; +DMASET: LD C,26 + JP ENTRY +; +; Delete the batch file created by SUBMIT. +; +DELBATCH: LD HL,BATCH ;is batch active? + LD A,(HL) + OR A + RET Z + LD (HL),0 ;yes, de-activate it. + XOR A + CALL DSKSEL ;select drive 0 for sure. + LD DE,BATCHFCB ;and delete this file. + CALL DELETE + LD A,(CDRIVE) ;reset current drive. + JP DSKSEL +; +; Check to two strings at (PATTRN1) and (PATTRN2). They must be +; the same or we halt.... +; +VERIFY: LD DE,PATTRN1 ;these are the serial number bytes. + LD HL,PATTRN2 ;ditto, but how could they be different? + LD B,6 ;6 bytes each. +VERIFY1:LD A,(DE) + CP (HL) + JP NZ,HALT ;jump to halt routine. + INC DE + INC HL + DEC B + JP NZ,VERIFY1 + RET +; +; Print back file name with a '?' to indicate a syntax error. +; +SYNERR: CALL CRLF ;end current line. + LD HL,(NAMEPNT) ;this points to name in error. +SYNERR1:LD A,(HL) ;print it until a space or null is found. + CP ' ' + JP Z,SYNERR2 + OR A + JP Z,SYNERR2 + PUSH HL + CALL PRINT + POP HL + INC HL + JP SYNERR1 +SYNERR2:LD A,'?' ;add trailing '?'. + CALL PRINT + CALL CRLF + CALL DELBATCH ;delete any batch file. + JP CMMND1 ;and restart from console input. +; +; Check character at (DE) for legal command input. Note that the +; zero flag is set if the character is a delimiter. +; +CHECK: LD A,(DE) + OR A + RET Z + CP ' ' ;control characters are not legal here. + JP C,SYNERR + RET Z ;check for valid delimiter. + CP '=' + RET Z + CP '_' + RET Z + CP '.' + RET Z + CP ':' + RET Z + CP 03BH ; ';' + RET Z + CP '<' + RET Z + CP '>' + RET Z + RET +; +; Get the next non-blank character from (DE). +; +NONBLANK: LD A,(DE) + OR A ;string ends with a null. + RET Z + CP ' ' + RET NZ + INC DE + JP NONBLANK +; +; Add (HL)=(HL)+(A) +; +ADDHL: ADD A,L + LD L,A + RET NC ;take care of any carry. + INC H + RET +; +; Convert the first name in (FCB). +; +CONVFST:LD A,0 +; +; Format a file name (convert * to '?', etc.). On return, +; (A)=0 is an unambigeous name was specified. Enter with (A) equal to +; the position within the fcb for the name (either 0 or 16). +; +CONVERT:LD HL,FCB + CALL ADDHL + PUSH HL + PUSH HL + XOR A + LD (CHGDRV),A ;initialize drive change flag. + LD HL,(INPOINT) ;set (HL) as pointer into input line. + EX DE,HL + CALL NONBLANK ;get next non-blank character. + EX DE,HL + LD (NAMEPNT),HL ;save pointer here for any error message. + EX DE,HL + POP HL + LD A,(DE) ;get first character. + OR A + JP Z,CONVRT1 + SBC A,'A'-1 ;might be a drive name, convert to binary. + LD B,A ;and save. + INC DE ;check next character for a ':'. + LD A,(DE) + CP ':' + JP Z,CONVRT2 + DEC DE ;nope, move pointer back to the start of the line. +CONVRT1:LD A,(CDRIVE) + LD (HL),A + JP CONVRT3 +CONVRT2:LD A,B + LD (CHGDRV),A ;set change in drives flag. + LD (HL),B + INC DE +; +; Convert the basic file name. +; +CONVRT3:LD B,08H +CONVRT4:CALL CHECK + JP Z,CONVRT8 + INC HL + CP '*' ;note that an '*' will fill the remaining + JP NZ,CONVRT5 ;field with '?'. + LD (HL),'?' + JP CONVRT6 +CONVRT5:LD (HL),A + INC DE +CONVRT6:DEC B + JP NZ,CONVRT4 +CONVRT7:CALL CHECK ;get next delimiter. + JP Z,GETEXT + INC DE + JP CONVRT7 +CONVRT8:INC HL ;blank fill the file name. + LD (HL),' ' + DEC B + JP NZ,CONVRT8 +; +; Get the extension and convert it. +; +GETEXT: LD B,03H + CP '.' + JP NZ,GETEXT5 + INC DE +GETEXT1:CALL CHECK + JP Z,GETEXT5 + INC HL + CP '*' + JP NZ,GETEXT2 + LD (HL),'?' + JP GETEXT3 +GETEXT2:LD (HL),A + INC DE +GETEXT3:DEC B + JP NZ,GETEXT1 +GETEXT4:CALL CHECK + JP Z,GETEXT6 + INC DE + JP GETEXT4 +GETEXT5:INC HL + LD (HL),' ' + DEC B + JP NZ,GETEXT5 +GETEXT6:LD B,3 +GETEXT7:INC HL + LD (HL),0 + DEC B + JP NZ,GETEXT7 + EX DE,HL + LD (INPOINT),HL ;save input line pointer. + POP HL +; +; Check to see if this is an ambigeous file name specification. +; Set the (A) register to non zero if it is. +; + LD BC,11 ;set name length. +GETEXT8:INC HL + LD A,(HL) + CP '?' ;any question marks? + JP NZ,GETEXT9 + INC B ;count them. +GETEXT9:DEC C + JP NZ,GETEXT8 + LD A,B + OR A + RET +; +; CP/M command table. Note commands can be either 3 or 4 characters long. +; +NUMCMDS .EQU 6 ;number of commands +CMDTBL: .TEXT "DIR " + .TEXT "ERA " + .TEXT "TYPE" + .TEXT "SAVE" + .TEXT "REN " + .TEXT "USER" +; +; The following six bytes must agree with those at (PATTRN2) +; or cp/m will HALT. Why? +; +PATTRN1:.DB 0,22,0,0,0,0 ;(* serial number bytes *). +; +; Search the command table for a match with what has just +; been entered. If a match is found, then we jump to the +; proper section. Else jump to (UNKNOWN). +; On return, the (C) register is set to the command number +; that matched (or NUMCMDS+1 if no match). +; +SEARCH: LD HL,CMDTBL + LD C,0 +SEARCH1:LD A,C + CP NUMCMDS ;this commands exists. + RET NC + LD DE,FCB+1 ;check this one. + LD B,4 ;max command length. +SEARCH2:LD A,(DE) + CP (HL) + JP NZ,SEARCH3 ;not a match. + INC DE + INC HL + DEC B + JP NZ,SEARCH2 + LD A,(DE) ;allow a 3 character command to match. + CP ' ' + JP NZ,SEARCH4 + LD A,C ;set return register for this command. + RET +SEARCH3:INC HL + DEC B + JP NZ,SEARCH3 +SEARCH4:INC C + JP SEARCH1 +; +; Set the input buffer to empty and then start the command +; processor (ccp). +; +CLEARBUF: XOR A + LD (INBUFF+1),A ;second byte is actual length. +; +;************************************************************** +;* +;* +;* C C P - C o n s o l e C o m m a n d P r o c e s s o r +;* +;************************************************************** +;* +COMMAND:LD SP,CCPSTACK ;setup stack area. + PUSH BC ;note that (C) should be equal to: + LD A,C ;(uuuudddd) where 'uuuu' is the user number + RRA ;and 'dddd' is the drive number. + RRA + RRA + RRA + AND 0FH ;isolate the user number. + LD E,A + CALL GETSETUC ;and set it. + CALL RESDSK ;reset the disk system. + LD (BATCH),A ;clear batch mode flag. + POP BC + LD A,C + AND 0FH ;isolate the drive number. + LD (CDRIVE),A ;and save. + CALL DSKSEL ;...and select. + LD A,(INBUFF+1) + OR A ;anything in input buffer already? + JP NZ,CMMND2 ;yes, we just process it. +; +; Entry point to get a command line from the console. +; +CMMND1: LD SP,CCPSTACK ;set stack straight. + CALL CRLF ;start a new line on the screen. + CALL GETDSK ;get current drive. + ADD A,'A' + CALL PRINT ;print current drive. + LD A,'>' + CALL PRINT ;and add prompt. + CALL GETINP ;get line from user. +; +; Process command line here. +; +CMMND2: LD DE,TBUFF + CALL DMASET ;set standard dma address. + CALL GETDSK + LD (CDRIVE),A ;set current drive. + CALL CONVFST ;convert name typed in. + CALL NZ,SYNERR ;wild cards are not allowed. + LD A,(CHGDRV) ;if a change in drives was indicated, + OR A ;then treat this as an unknown command + JP NZ,UNKNOWN ;which gets executed. + CALL SEARCH ;else search command table for a match. +; +; Note that an unknown command returns +; with (A) pointing to the last address +; in our table which is (UNKNOWN). +; + LD HL,CMDADR ;now, look thru our address table for command (A). + LD E,A ;set (DE) to command number. + LD D,0 + ADD HL,DE + ADD HL,DE ;(HL)=(CMDADR)+2*(command number). + LD A,(HL) ;now pick out this address. + INC HL + LD H,(HL) + LD L,A + JP (HL) ;now execute it. +; +; CP/M command address table. +; +CMDADR: .DW DIRECT,ERASE,TYPE,SAVE + .DW RENAME,USER,UNKNOWN +; +; Halt the system. Reason for this is unknown at present. +; +HALT: LD HL,76F3H ;'DI HLT' instructions. + LD (CBASE),HL + LD HL,CBASE + JP (HL) +; +; Read error while TYPEing a file. +; +RDERROR:LD BC,RDERR + JP PLINE +RDERR: .TEXT "Read error" + .DB 0 +; +; Required file was not located. +; +NONE: LD BC,NOFILE + JP PLINE +NOFILE: .TEXT "No file" + .DB 0 +; +; Decode a command of the form 'A>filename number{ filename}. +; Note that a drive specifier is not allowed on the first file +; name. On return, the number is in register (A). Any error +; causes 'filename?' to be printed and the command is aborted. +; +DECODE: CALL CONVFST ;convert filename. + LD A,(CHGDRV) ;do not allow a drive to be specified. + OR A + JP NZ,SYNERR + LD HL,FCB+1 ;convert number now. + LD BC,11 ;(B)=sum register, (C)=max digit count. +DECODE1:LD A,(HL) + CP ' ' ;a space terminates the numeral. + JP Z,DECODE3 + INC HL + SUB '0' ;make binary from ascii. + CP 10 ;legal digit? + JP NC,SYNERR + LD D,A ;yes, save it in (D). + LD A,B ;compute (B)=(B)*10 and check for overflow. + AND 0E0H + JP NZ,SYNERR + LD A,B + RLCA + RLCA + RLCA ;(A)=(B)*8 + ADD A,B ;.......*9 + JP C,SYNERR + ADD A,B ;.......*10 + JP C,SYNERR + ADD A,D ;add in new digit now. +DECODE2:JP C,SYNERR + LD B,A ;and save result. + DEC C ;only look at 11 digits. + JP NZ,DECODE1 + RET +DECODE3:LD A,(HL) ;spaces must follow (why?). + CP ' ' + JP NZ,SYNERR + INC HL +DECODE4:DEC C + JP NZ,DECODE3 + LD A,B ;set (A)=the numeric value entered. + RET +; +; Move 3 bytes from (HL) to (DE). Note that there is only +; one reference to this at (A2D5h). +; +MOVE3: LD B,3 +; +; Move (B) bytes from (HL) to (DE). +; +HL2DE: LD A,(HL) + LD (DE),A + INC HL + INC DE + DEC B + JP NZ,HL2DE + RET +; +; Compute (HL)=(TBUFF)+(A)+(C) and get the byte that's here. +; +EXTRACT:LD HL,TBUFF + ADD A,C + CALL ADDHL + LD A,(HL) + RET +; +; Check drive specified. If it means a change, then the new +; drive will be selected. In any case, the drive byte of the +; fcb will be set to null (means use current drive). +; +DSELECT:XOR A ;null out first byte of fcb. + LD (FCB),A + LD A,(CHGDRV) ;a drive change indicated? + OR A + RET Z + DEC A ;yes, is it the same as the current drive? + LD HL,CDRIVE + CP (HL) + RET Z + JP DSKSEL ;no. Select it then. +; +; Check the drive selection and reset it to the previous +; drive if it was changed for the preceeding command. +; +RESETDR:LD A,(CHGDRV) ;drive change indicated? + OR A + RET Z + DEC A ;yes, was it a different drive? + LD HL,CDRIVE + CP (HL) + RET Z + LD A,(CDRIVE) ;yes, re-select our old drive. + JP DSKSEL +; +;************************************************************** +;* +;* D I R E C T O R Y C O M M A N D +;* +;************************************************************** +; +DIRECT: CALL CONVFST ;convert file name. + CALL DSELECT ;select indicated drive. + LD HL,FCB+1 ;was any file indicated? + LD A,(HL) + CP ' ' + JP NZ,DIRECT2 + LD B,11 ;no. Fill field with '?' - same as *.*. +DIRECT1:LD (HL),'?' + INC HL + DEC B + JP NZ,DIRECT1 +DIRECT2:LD E,0 ;set initial cursor position. + PUSH DE + CALL SRCHFCB ;get first file name. + CALL Z,NONE ;none found at all? +DIRECT3:JP Z,DIRECT9 ;terminate if no more names. + LD A,(RTNCODE) ;get file's position in segment (0-3). + RRCA + RRCA + RRCA + AND 60H ;(A)=position*32 + LD C,A + LD A,10 + CALL EXTRACT ;extract the tenth entry in fcb. + RLA ;check system file status bit. + JP C,DIRECT8 ;we don't list them. + POP DE + LD A,E ;bump name count. + INC E + PUSH DE + AND 03H ;at end of line? + PUSH AF + JP NZ,DIRECT4 + CALL CRLF ;yes, end this line and start another. + PUSH BC + CALL GETDSK ;start line with ('A:'). + POP BC + ADD A,'A' + CALL PRINTB + LD A,':' + CALL PRINTB + JP DIRECT5 +DIRECT4:CALL SPACE ;add seperator between file names. + LD A,':' + CALL PRINTB +DIRECT5:CALL SPACE + LD B,1 ;'extract' each file name character at a time. +DIRECT6:LD A,B + CALL EXTRACT + AND 7FH ;strip bit 7 (status bit). + CP ' ' ;are we at the end of the name? + JP NZ,DRECT65 + POP AF ;yes, don't print spaces at the end of a line. + PUSH AF + CP 3 + JP NZ,DRECT63 + LD A,9 ;first check for no extension. + CALL EXTRACT + AND 7FH + CP ' ' + JP Z,DIRECT7 ;don't print spaces. +DRECT63:LD A,' ' ;else print them. +DRECT65:CALL PRINTB + INC B ;bump to next character psoition. + LD A,B + CP 12 ;end of the name? + JP NC,DIRECT7 + CP 9 ;nope, starting extension? + JP NZ,DIRECT6 + CALL SPACE ;yes, add seperating space. + JP DIRECT6 +DIRECT7:POP AF ;get the next file name. +DIRECT8:CALL CHKCON ;first check console, quit on anything. + JP NZ,DIRECT9 + CALL SRCHNXT ;get next name. + JP DIRECT3 ;and continue with our list. +DIRECT9:POP DE ;restore the stack and return to command level. + JP GETBACK +; +;************************************************************** +;* +;* E R A S E C O M M A N D +;* +;************************************************************** +; +ERASE: CALL CONVFST ;convert file name. + CP 11 ;was '*.*' entered? + JP NZ,ERASE1 + LD BC,YESNO ;yes, ask for confirmation. + CALL PLINE + CALL GETINP + LD HL,INBUFF+1 + DEC (HL) ;must be exactly 'y'. + JP NZ,CMMND1 + INC HL + LD A,(HL) + CP 'Y' + JP NZ,CMMND1 + INC HL + LD (INPOINT),HL ;save input line pointer. +ERASE1: CALL DSELECT ;select desired disk. + LD DE,FCB + CALL DELETE ;delete the file. + INC A + CALL Z,NONE ;not there? + JP GETBACK ;return to command level now. +YESNO: .TEXT "All (y/n)?" + .DB 0 +; +;************************************************************** +;* +;* T Y P E C O M M A N D +;* +;************************************************************** +; +TYPE: CALL CONVFST ;convert file name. + JP NZ,SYNERR ;wild cards not allowed. + CALL DSELECT ;select indicated drive. + CALL OPENFCB ;open the file. + JP Z,TYPE5 ;not there? + CALL CRLF ;ok, start a new line on the screen. + LD HL,NBYTES ;initialize byte counter. + LD (HL),0FFH ;set to read first sector. +TYPE1: LD HL,NBYTES +TYPE2: LD A,(HL) ;have we written the entire sector? + CP 128 + JP C,TYPE3 + PUSH HL ;yes, read in the next one. + CALL READFCB + POP HL + JP NZ,TYPE4 ;end or error? + XOR A ;ok, clear byte counter. + LD (HL),A +TYPE3: INC (HL) ;count this byte. + LD HL,TBUFF ;and get the (A)th one from the buffer (TBUFF). + CALL ADDHL + LD A,(HL) + CP CNTRLZ ;end of file mark? + JP Z,GETBACK + CALL PRINT ;no, print it. + CALL CHKCON ;check console, quit if anything ready. + JP NZ,GETBACK + JP TYPE1 +; +; Get here on an end of file or read error. +; +TYPE4: DEC A ;read error? + JP Z,GETBACK + CALL RDERROR ;yes, print message. +TYPE5: CALL RESETDR ;and reset proper drive + JP SYNERR ;now print file name with problem. +; +;************************************************************** +;* +;* S A V E C O M M A N D +;* +;************************************************************** +; +SAVE: CALL DECODE ;get numeric number that follows SAVE. + PUSH AF ;save number of pages to write. + CALL CONVFST ;convert file name. + JP NZ,SYNERR ;wild cards not allowed. + CALL DSELECT ;select specified drive. + LD DE,FCB ;now delete this file. + PUSH DE + CALL DELETE + POP DE + CALL CREATE ;and create it again. + JP Z,SAVE3 ;can't create? + XOR A ;clear record number byte. + LD (FCB+32),A + POP AF ;convert pages to sectors. + LD L,A + LD H,0 + ADD HL,HL ;(HL)=number of sectors to write. + LD DE,TBASE ;and we start from here. +SAVE1: LD A,H ;done yet? + OR L + JP Z,SAVE2 + DEC HL ;nope, count this and compute the start + PUSH HL ;of the next 128 byte sector. + LD HL,128 + ADD HL,DE + PUSH HL ;save it and set the transfer address. + CALL DMASET + LD DE,FCB ;write out this sector now. + CALL WRTREC + POP DE ;reset (DE) to the start of the last sector. + POP HL ;restore sector count. + JP NZ,SAVE3 ;write error? + JP SAVE1 +; +; Get here after writing all of the file. +; +SAVE2: LD DE,FCB ;now close the file. + CALL CLOSE + INC A ;did it close ok? + JP NZ,SAVE4 +; +; Print out error message (no space). +; +SAVE3: LD BC,NOSPACE + CALL PLINE +SAVE4: CALL STDDMA ;reset the standard dma address. + JP GETBACK +NOSPACE:.TEXT "No space" + .DB 0 +; +;************************************************************** +;* +;* R E N A M E C O M M A N D +;* +;************************************************************** +; +RENAME: CALL CONVFST ;convert first file name. + JP NZ,SYNERR ;wild cards not allowed. + LD A,(CHGDRV) ;remember any change in drives specified. + PUSH AF + CALL DSELECT ;and select this drive. + CALL SRCHFCB ;is this file present? + JP NZ,RENAME6 ;yes, print error message. + LD HL,FCB ;yes, move this name into second slot. + LD DE,FCB+16 + LD B,16 + CALL HL2DE + LD HL,(INPOINT) ;get input pointer. + EX DE,HL + CALL NONBLANK ;get next non blank character. + CP '=' ;only allow an '=' or '_' seperator. + JP Z,RENAME1 + CP '_' + JP NZ,RENAME5 +RENAME1:EX DE,HL + INC HL ;ok, skip seperator. + LD (INPOINT),HL ;save input line pointer. + CALL CONVFST ;convert this second file name now. + JP NZ,RENAME5 ;again, no wild cards. + POP AF ;if a drive was specified, then it + LD B,A ;must be the same as before. + LD HL,CHGDRV + LD A,(HL) + OR A + JP Z,RENAME2 + CP B + LD (HL),B + JP NZ,RENAME5 ;they were different, error. +RENAME2:LD (HL),B ; reset as per the first file specification. + XOR A + LD (FCB),A ;clear the drive byte of the fcb. +RENAME3:CALL SRCHFCB ;and go look for second file. + JP Z,RENAME4 ;doesn't exist? + LD DE,FCB + CALL RENAM ;ok, rename the file. + JP GETBACK +; +; Process rename errors here. +; +RENAME4:CALL NONE ;file not there. + JP GETBACK +RENAME5:CALL RESETDR ;bad command format. + JP SYNERR +RENAME6:LD BC,EXISTS ;destination file already exists. + CALL PLINE + JP GETBACK +EXISTS: .TEXT "File exists" + .DB 0 +; +;************************************************************** +;* +;* U S E R C O M M A N D +;* +;************************************************************** +; +USER: CALL DECODE ;get numeric value following command. + CP 16 ;legal user number? + JP NC,SYNERR + LD E,A ;yes but is there anything else? + LD A,(FCB+1) + CP ' ' + JP Z,SYNERR ;yes, that is not allowed. + CALL GETSETUC ;ok, set user code. + JP GETBACK1 +; +;************************************************************** +;* +;* T R A N S I A N T P R O G R A M C O M M A N D +;* +;************************************************************** +; +UNKNOWN:CALL VERIFY ;check for valid system (why?). + LD A,(FCB+1) ;anything to execute? + CP ' ' + JP NZ,UNKWN1 + LD A,(CHGDRV) ;nope, only a drive change? + OR A + JP Z,GETBACK1 ;neither??? + DEC A + LD (CDRIVE),A ;ok, store new drive. + CALL MOVECD ;set (TDRIVE) also. + CALL DSKSEL ;and select this drive. + JP GETBACK1 ;then return. +; +; Here a file name was typed. Prepare to execute it. +; +UNKWN1: LD DE,FCB+9 ;an extension specified? + LD A,(DE) + CP ' ' + JP NZ,SYNERR ;yes, not allowed. +UNKWN2: PUSH DE + CALL DSELECT ;select specified drive. + POP DE + LD HL,COMFILE ;set the extension to 'COM'. + CALL MOVE3 + CALL OPENFCB ;and open this file. + JP Z,UNKWN9 ;not present? +; +; Load in the program. +; + LD HL,TBASE ;store the program starting here. +UNKWN3: PUSH HL + EX DE,HL + CALL DMASET ;set transfer address. + LD DE,FCB ;and read the next record. + CALL RDREC + JP NZ,UNKWN4 ;end of file or read error? + POP HL ;nope, bump pointer for next sector. + LD DE,128 + ADD HL,DE + LD DE,CBASE ;enough room for the whole file? + LD A,L + SUB E + LD A,H + SBC A,D + JP NC,UNKWN0 ;no, it can't fit. + JP UNKWN3 +; +; Get here after finished reading. +; +UNKWN4: POP HL + DEC A ;normal end of file? + JP NZ,UNKWN0 + CALL RESETDR ;yes, reset previous drive. + CALL CONVFST ;convert the first file name that follows + LD HL,CHGDRV ;command name. + PUSH HL + LD A,(HL) ;set drive code in default fcb. + LD (FCB),A + LD A,16 ;put second name 16 bytes later. + CALL CONVERT ;convert second file name. + POP HL + LD A,(HL) ;and set the drive for this second file. + LD (FCB+16),A + XOR A ;clear record byte in fcb. + LD (FCB+32),A + LD DE,TFCB ;move it into place at(005Ch). + LD HL,FCB + LD B,33 + CALL HL2DE + LD HL,INBUFF+2 ;now move the remainder of the input +UNKWN5: LD A,(HL) ;line down to (0080h). Look for a non blank. + OR A ;or a null. + JP Z,UNKWN6 + CP ' ' + JP Z,UNKWN6 + INC HL + JP UNKWN5 +; +; Do the line move now. It ends in a null byte. +; +UNKWN6: LD B,0 ;keep a character count. + LD DE,TBUFF+1 ;data gets put here. +UNKWN7: LD A,(HL) ;move it now. + LD (DE),A + OR A + JP Z,UNKWN8 + INC B + INC HL + INC DE + JP UNKWN7 +UNKWN8: LD A,B ;now store the character count. + LD (TBUFF),A + CALL CRLF ;clean up the screen. + CALL STDDMA ;set standard transfer address. + CALL SETCDRV ;reset current drive. + CALL TBASE ;and execute the program. +; +; Transiant programs return here (or reboot). +; + LD SP,BATCH ;set stack first off. + CALL MOVECD ;move current drive into place (TDRIVE). + CALL DSKSEL ;and reselect it. + JP CMMND1 ;back to comand mode. +; +; Get here if some error occured. +; +UNKWN9: CALL RESETDR ;inproper format. + JP SYNERR +UNKWN0: LD BC,BADLOAD ;read error or won't fit. + CALL PLINE + JP GETBACK +BADLOAD:.TEXT "Bad load" + .DB 0 +COMFILE:.TEXT "COM" ;command file extension. +; +; Get here to return to command level. We will reset the +; previous active drive and then either return to command +; level directly or print error message and then return. +; +GETBACK:CALL RESETDR ;reset previous drive. +GETBACK1: CALL CONVFST ;convert first name in (FCB). + LD A,(FCB+1) ;if this was just a drive change request, + SUB ' ' ;make sure it was valid. + LD HL,CHGDRV + OR (HL) + JP NZ,SYNERR + JP CMMND1 ;ok, return to command level. +; +; ccp stack area. +; + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +CCPSTACK .EQU $ ;end of ccp stack area. +; +; Batch (or SUBMIT) processing information storage. +; +BATCH: .DB 0 ;batch mode flag (0=not active). +BATCHFCB: .DB 0, + .TEXT "$$$ SUB" + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +; +; File control block setup by the CCP. +; +FCB: .DB 0 + .TEXT " " + .DB 0,0,0,0,0 + .TEXT " " + .DB 0,0,0,0,0 +RTNCODE:.DB 0 ;status returned from bdos call. +CDRIVE: .DB 0 ;currently active drive. +CHGDRV: .DB 0 ;change in drives flag (0=no change). +NBYTES: .DW 0 ;byte counter used by TYPE. +; +; Room for expansion? +; + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0 +; +; Note that the following six bytes must match those at +; (PATTRN1) or cp/m will HALT. Why? +; +PATTRN2:.DB 0,22,0,0,0,0 ;(* serial number bytes *). +; +;************************************************************** +;* +;* B D O S E N T R Y +;* +;************************************************************** +; +FBASE: JP FBASE1 +; +; Bdos error table. +; +BADSCTR:.DW ERROR1 ;bad sector on read or write. +BADSLCT:.DW ERROR2 ;bad disk select. +RODISK: .DW ERROR3 ;disk is read only. +ROFILE: .DW ERROR4 ;file is read only. +; +; Entry into bdos. (DE) or (E) are the parameters passed. The +; function number desired is in register (C). +; +FBASE1: EX DE,HL ;save the (DE) parameters. + LD (PARAMS),HL + EX DE,HL + LD A,E ;and save register (E) in particular. + LD (EPARAM),A + LD HL,0 + LD (STATUS),HL ;clear return status. + ADD HL,SP + LD (USRSTACK),HL ;save users stack pointer. + LD SP,STKAREA ;and set our own. + XOR A ;clear auto select storage space. + LD (AUTOFLAG),A + LD (AUTO),A + LD HL,GOBACK ;set return address. + PUSH HL + LD A,C ;get function number. + CP NFUNCTS ;valid function number? + RET NC + LD C,E ;keep single register function here. + LD HL,FUNCTNS ;now look thru the function table. + LD E,A + LD D,0 ;(DE)=function number. + ADD HL,DE + ADD HL,DE ;(HL)=(start of table)+2*(function number). + LD E,(HL) + INC HL + LD D,(HL) ;now (DE)=address for this function. + LD HL,(PARAMS) ;retrieve parameters. + EX DE,HL ;now (DE) has the original parameters. + JP (HL) ;execute desired function. +; +; BDOS function jump table. +; +NFUNCTS .EQU 41 ;number of functions in followin table. +; +FUNCTNS:.DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB + .DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL + .DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE + .DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR + .DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN + .DW RTN,WTSPECL +; +; Bdos error message section. +; +ERROR1: LD HL,BADSEC ;bad sector message. + CALL PRTERR ;print it and get a 1 char responce. + CP CNTRLC ;re-boot request (control-c)? + JP Z,0 ;yes. + RET ;no, return to retry i/o function. +; +ERROR2: LD HL,BADSEL ;bad drive selected. + JP ERROR5 +; +ERROR3: LD HL,DISKRO ;disk is read only. + JP ERROR5 +; +ERROR4: LD HL,FILERO ;file is read only. +; +ERROR5: CALL PRTERR + JP 0 ;always reboot on these errors. +; +BDOSERR:.TEXT "Bdos Err On " +BDOSDRV:.TEXT " : $" +BADSEC: .TEXT "Bad Sector$" +BADSEL: .TEXT "Select$" +FILERO: .TEXT "File " +DISKRO: .TEXT "R/O$" +; +; Print bdos error message. +; +PRTERR: PUSH HL ;save second message pointer. + CALL OUTCRLF ;send (cr)(lf). + LD A,(ACTIVE) ;get active drive. + ADD A,'A' ;make ascii. + LD (BDOSDRV),A ;and put in message. + LD BC,BDOSERR ;and print it. + CALL PRTMESG + POP BC ;print second message line now. + CALL PRTMESG +; +; Get an input character. We will check our 1 character +; buffer first. This may be set by the console status routine. +; +GETCHAR:LD HL,CHARBUF ;check character buffer. + LD A,(HL) ;anything present already? + LD (HL),0 ;...either case clear it. + OR A + RET NZ ;yes, use it. + JP CONIN ;nope, go get a character responce. +; +; Input and echo a character. +; +GETECHO:CALL GETCHAR ;input a character. + CALL CHKCHAR ;carriage control? + RET C ;no, a regular control char so don't echo. + PUSH AF ;ok, save character now. + LD C,A + CALL OUTCON ;and echo it. + POP AF ;get character and return. + RET +; +; Check character in (A). Set the zero flag on a carriage +; control character and the carry flag on any other control +; character. +; +CHKCHAR:CP CR ;check for carriage return, line feed, backspace, + RET Z ;or a tab. + CP LF + RET Z + CP TAB + RET Z + CP BS + RET Z + CP ' ' ;other control char? Set carry flag. + RET +; +; Check the console during output. Halt on a control-s, then +; reboot on a control-c. If anything else is ready, clear the +; zero flag and return (the calling routine may want to do +; something). +; +CKCONSOL: LD A,(CHARBUF) ;check buffer. + OR A ;if anything, just return without checking. + JP NZ,CKCON2 + CALL CONST ;nothing in buffer. Check console. + AND 01H ;look at bit 0. + RET Z ;return if nothing. + CALL CONIN ;ok, get it. + CP CNTRLS ;if not control-s, return with zero cleared. + JP NZ,CKCON1 + CALL CONIN ;halt processing until another char + CP CNTRLC ;is typed. Control-c? + JP Z,0 ;yes, reboot now. + XOR A ;no, just pretend nothing was ever ready. + RET +CKCON1: LD (CHARBUF),A ;save character in buffer for later processing. +CKCON2: LD A,1 ;set (A) to non zero to mean something is ready. + RET +; +; Output (C) to the screen. If the printer flip-flop flag +; is set, we will send character to printer also. The console +; will be checked in the process. +; +OUTCHAR:LD A,(OUTFLAG) ;check output flag. + OR A ;anything and we won't generate output. + JP NZ,OUTCHR1 + PUSH BC + CALL CKCONSOL ;check console (we don't care whats there). + POP BC + PUSH BC + CALL CONOUT ;output (C) to the screen. + POP BC + PUSH BC + LD A,(PRTFLAG) ;check printer flip-flop flag. + OR A + CALL NZ,LIST ;print it also if non-zero. + POP BC +OUTCHR1:LD A,C ;update cursors position. + LD HL,CURPOS + CP DEL ;rubouts don't do anything here. + RET Z + INC (HL) ;bump line pointer. + CP ' ' ;and return if a normal character. + RET NC + DEC (HL) ;restore and check for the start of the line. + LD A,(HL) + OR A + RET Z ;ingnore control characters at the start of the line. + LD A,C + CP BS ;is it a backspace? + JP NZ,OUTCHR2 + DEC (HL) ;yes, backup pointer. + RET +OUTCHR2:CP LF ;is it a line feed? + RET NZ ;ignore anything else. + LD (HL),0 ;reset pointer to start of line. + RET +; +; Output (A) to the screen. If it is a control character +; (other than carriage control), use ^x format. +; +SHOWIT: LD A,C + CALL CHKCHAR ;check character. + JP NC,OUTCON ;not a control, use normal output. + PUSH AF + LD C,'^' ;for a control character, preceed it with '^'. + CALL OUTCHAR + POP AF + OR '@' ;and then use the letter equivelant. + LD C,A +; +; Function to output (C) to the console device and expand tabs +; if necessary. +; +OUTCON: LD A,C + CP TAB ;is it a tab? + JP NZ,OUTCHAR ;use regular output. +OUTCON1:LD C,' ' ;yes it is, use spaces instead. + CALL OUTCHAR + LD A,(CURPOS) ;go until the cursor is at a multiple of 8 + + AND 07H ;position. + JP NZ,OUTCON1 + RET +; +; Echo a backspace character. Erase the prevoius character +; on the screen. +; +BACKUP: CALL BACKUP1 ;backup the screen 1 place. + LD C,' ' ;then blank that character. + CALL CONOUT +BACKUP1:LD C,BS ;then back space once more. + JP CONOUT +; +; Signal a deleted line. Print a '#' at the end and start +; over. +; +NEWLINE:LD C,'#' + CALL OUTCHAR ;print this. + CALL OUTCRLF ;start new line. +NEWLN1: LD A,(CURPOS) ;move the cursor to the starting position. + LD HL,STARTING + CP (HL) + RET NC ;there yet? + LD C,' ' + CALL OUTCHAR ;nope, keep going. + JP NEWLN1 +; +; Output a (cr) (lf) to the console device (screen). +; +OUTCRLF:LD C,CR + CALL OUTCHAR + LD C,LF + JP OUTCHAR +; +; Print message pointed to by (BC). It will end with a '$'. +; +PRTMESG:LD A,(BC) ;check for terminating character. + CP '$' + RET Z + INC BC + PUSH BC ;otherwise, bump pointer and print it. + LD C,A + CALL OUTCON + POP BC + JP PRTMESG +; +; Function to execute a buffered read. +; +RDBUFF: LD A,(CURPOS) ;use present location as starting one. + LD (STARTING),A + LD HL,(PARAMS) ;get the maximum buffer space. + LD C,(HL) + INC HL ;point to first available space. + PUSH HL ;and save. + LD B,0 ;keep a character count. +RDBUF1: PUSH BC + PUSH HL +RDBUF2: CALL GETCHAR ;get the next input character. + AND 7FH ;strip bit 7. + POP HL ;reset registers. + POP BC + CP CR ;en of the line? + JP Z,RDBUF17 + CP LF + JP Z,RDBUF17 + CP BS ;how about a backspace? + JP NZ,RDBUF3 + LD A,B ;yes, but ignore at the beginning of the line. + OR A + JP Z,RDBUF1 + DEC B ;ok, update counter. + LD A,(CURPOS) ;if we backspace to the start of the line, + LD (OUTFLAG),A ;treat as a cancel (control-x). + JP RDBUF10 +RDBUF3: CP DEL ;user typed a rubout? + JP NZ,RDBUF4 + LD A,B ;ignore at the start of the line. + OR A + JP Z,RDBUF1 + LD A,(HL) ;ok, echo the prevoius character. + DEC B ;and reset pointers (counters). + DEC HL + JP RDBUF15 +RDBUF4: CP CNTRLE ;physical end of line? + JP NZ,RDBUF5 + PUSH BC ;yes, do it. + PUSH HL + CALL OUTCRLF + XOR A ;and update starting position. + LD (STARTING),A + JP RDBUF2 +RDBUF5: CP CNTRLP ;control-p? + JP NZ,RDBUF6 + PUSH HL ;yes, flip the print flag filp-flop byte. + LD HL,PRTFLAG + LD A,1 ;PRTFLAG=1-PRTFLAG + SUB (HL) + LD (HL),A + POP HL + JP RDBUF1 +RDBUF6: CP CNTRLX ;control-x (cancel)? + JP NZ,RDBUF8 + POP HL +RDBUF7: LD A,(STARTING) ;yes, backup the cursor to here. + LD HL,CURPOS + CP (HL) + JP NC,RDBUFF ;done yet? + DEC (HL) ;no, decrement pointer and output back up one space. + CALL BACKUP + JP RDBUF7 +RDBUF8: CP CNTRLU ;cntrol-u (cancel line)? + JP NZ,RDBUF9 + CALL NEWLINE ;start a new line. + POP HL + JP RDBUFF +RDBUF9: CP CNTRLR ;control-r? + JP NZ,RDBUF14 +RDBUF10:PUSH BC ;yes, start a new line and retype the old one. + CALL NEWLINE + POP BC + POP HL + PUSH HL + PUSH BC +RDBUF11:LD A,B ;done whole line yet? + OR A + JP Z,RDBUF12 + INC HL ;nope, get next character. + LD C,(HL) + DEC B ;count it. + PUSH BC + PUSH HL + CALL SHOWIT ;and display it. + POP HL + POP BC + JP RDBUF11 +RDBUF12:PUSH HL ;done with line. If we were displaying + LD A,(OUTFLAG) ;then update cursor position. + OR A + JP Z,RDBUF2 + LD HL,CURPOS ;because this line is shorter, we must + SUB (HL) ;back up the cursor (not the screen however) + LD (OUTFLAG),A ;some number of positions. +RDBUF13:CALL BACKUP ;note that as long as (OUTFLAG) is non + LD HL,OUTFLAG ;zero, the screen will not be changed. + DEC (HL) + JP NZ,RDBUF13 + JP RDBUF2 ;now just get the next character. +; +; Just a normal character, put this in our buffer and echo. +; +RDBUF14:INC HL + LD (HL),A ;store character. + INC B ;and count it. +RDBUF15:PUSH BC + PUSH HL + LD C,A ;echo it now. + CALL SHOWIT + POP HL + POP BC + LD A,(HL) ;was it an abort request? + CP CNTRLC ;control-c abort? + LD A,B + JP NZ,RDBUF16 + CP 1 ;only if at start of line. + JP Z,0 +RDBUF16:CP C ;nope, have we filled the buffer? + JP C,RDBUF1 +RDBUF17:POP HL ;yes end the line and return. + LD (HL),B + LD C,CR + JP OUTCHAR ;output (cr) and return. +; +; Function to get a character from the console device. +; +GETCON: CALL GETECHO ;get and echo. + JP SETSTAT ;save status and return. +; +; Function to get a character from the tape reader device. +; +GETRDR: CALL READER ;get a character from reader, set status and return. + JP SETSTAT +; +; Function to perform direct console i/o. If (C) contains (FF) +; then this is an input request. If (C) contains (FE) then +; this is a status request. Otherwise we are to output (C). +; +DIRCIO: LD A,C ;test for (FF). + INC A + JP Z,DIRC1 + INC A ;test for (FE). + JP Z,CONST + JP CONOUT ;just output (C). +DIRC1: CALL CONST ;this is an input request. + OR A + JP Z,GOBACK1 ;not ready? Just return (directly). + CALL CONIN ;yes, get character. + JP SETSTAT ;set status and return. +; +; Function to return the i/o byte. +; +GETIOB: LD A,(IOBYTE) + JP SETSTAT +; +; Function to set the i/o byte. +; +SETIOB: LD HL,IOBYTE + LD (HL),C + RET +; +; Function to print the character string pointed to by (DE) +; on the console device. The string ends with a '$'. +; +PRTSTR: EX DE,HL + LD C,L + LD B,H ;now (BC) points to it. + JP PRTMESG +; +; Function to interigate the console device. +; +GETCSTS:CALL CKCONSOL +; +; Get here to set the status and return to the cleanup +; section. Then back to the user. +; +SETSTAT:LD (STATUS),A +RTN: RET +; +; Set the status to 1 (read or write error code). +; +IOERR1: LD A,1 + JP SETSTAT +; +OUTFLAG:.DB 0 ;output flag (non zero means no output). +STARTING: .DB 2 ;starting position for cursor. +CURPOS: .DB 0 ;cursor position (0=start of line). +PRTFLAG:.DB 0 ;printer flag (control-p toggle). List if non zero. +CHARBUF:.DB 0 ;single input character buffer. +; +; Stack area for BDOS calls. +; +USRSTACK: .DW 0 ;save users stack pointer here. +; + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +STKAREA .EQU $ ;end of stack area. +; +USERNO: .DB 0 ;current user number. +ACTIVE: .DB 0 ;currently active drive. +PARAMS: .DW 0 ;save (DE) parameters here on entry. +STATUS: .DW 0 ;status returned from bdos function. +; +; Select error occured, jump to error routine. +; +SLCTERR:LD HL,BADSLCT +; +; Jump to (HL) indirectly. +; +JUMPHL: LD E,(HL) + INC HL + LD D,(HL) ;now (DE) contain the desired address. + EX DE,HL + JP (HL) +; +; Block move. (DE) to (HL), (C) bytes total. +; +DE2HL: INC C ;is count down to zero? +DE2HL1: DEC C + RET Z ;yes, we are done. + LD A,(DE) ;no, move one more byte. + LD (HL),A + INC DE + INC HL + JP DE2HL1 ;and repeat. +; +; Select the desired drive. +; +SELECT: LD A,(ACTIVE) ;get active disk. + LD C,A + CALL SELDSK ;select it. + LD A,H ;valid drive? + OR L ;valid drive? + RET Z ;return if not. +; +; Here, the BIOS returned the address of the parameter block +; in (HL). We will extract the necessary pointers and save them. +; + LD E,(HL) ;yes, get address of translation table into (DE). + INC HL + LD D,(HL) + INC HL + LD (SCRATCH1),HL ;save pointers to scratch areas. + INC HL + INC HL + LD (SCRATCH2),HL ;ditto. + INC HL + INC HL + LD (SCRATCH3),HL ;ditto. + INC HL + INC HL + EX DE,HL ;now save the translation table address. + LD (XLATE),HL + LD HL,DIRBUF ;put the next 8 bytes here. + LD C,8 ;they consist of the directory buffer + CALL DE2HL ;pointer, parameter block pointer, + LD HL,(DISKPB) ;check and allocation vectors. + EX DE,HL + LD HL,SECTORS ;move parameter block into our ram. + LD C,15 ;it is 15 bytes long. + CALL DE2HL + LD HL,(DSKSIZE) ;check disk size. + LD A,H ;more than 256 blocks on this? + LD HL,BIGDISK + LD (HL),0FFH ;set to samll. + OR A + JP Z,SELECT1 + LD (HL),0 ;wrong, set to large. +SELECT1:LD A,0FFH ;clear the zero flag. + OR A + RET +; +; Routine to home the disk track head and clear pointers. +; +HOMEDRV:CALL HOME ;home the head. + XOR A + LD HL,(SCRATCH2) ;set our track pointer also. + LD (HL),A + INC HL + LD (HL),A + LD HL,(SCRATCH3) ;and our sector pointer. + LD (HL),A + INC HL + LD (HL),A + RET +; +; Do the actual disk read and check the error return status. +; +DOREAD: CALL READ + JP IORET +; +; Do the actual disk write and handle any bios error. +; +DOWRITE:CALL WRITE +IORET: OR A + RET Z ;return unless an error occured. + LD HL,BADSCTR ;bad read/write on this sector. + JP JUMPHL +; +; Routine to select the track and sector that the desired +; block number falls in. +; +TRKSEC: LD HL,(FILEPOS) ;get position of last accessed file + LD C,2 ;in directory and compute sector #. + CALL SHIFTR ;sector #=file-position/4. + LD (BLKNMBR),HL ;save this as the block number of interest. + LD (CKSUMTBL),HL ;what's it doing here too? +; +; if the sector number has already been set (BLKNMBR), enter +; at this point. +; +TRKSEC1:LD HL,BLKNMBR + LD C,(HL) ;move sector number into (BC). + INC HL + LD B,(HL) + LD HL,(SCRATCH3) ;get current sector number and + LD E,(HL) ;move this into (DE). + INC HL + LD D,(HL) + LD HL,(SCRATCH2) ;get current track number. + LD A,(HL) ;and this into (HL). + INC HL + LD H,(HL) + LD L,A +TRKSEC2:LD A,C ;is desired sector before current one? + SUB E + LD A,B + SBC A,D + JP NC,TRKSEC3 + PUSH HL ;yes, decrement sectors by one track. + LD HL,(SECTORS) ;get sectors per track. + LD A,E + SUB L + LD E,A + LD A,D + SBC A,H + LD D,A ;now we have backed up one full track. + POP HL + DEC HL ;adjust track counter. + JP TRKSEC2 +TRKSEC3:PUSH HL ;desired sector is after current one. + LD HL,(SECTORS) ;get sectors per track. + ADD HL,DE ;bump sector pointer to next track. + JP C,TRKSEC4 + LD A,C ;is desired sector now before current one? + SUB L + LD A,B + SBC A,H + JP C,TRKSEC4 + EX DE,HL ;not yes, increment track counter + POP HL ;and continue until it is. + INC HL + JP TRKSEC3 +; +; here we have determined the track number that contains the +; desired sector. +; +TRKSEC4:POP HL ;get track number (HL). + PUSH BC + PUSH DE + PUSH HL + EX DE,HL + LD HL,(OFFSET) ;adjust for first track offset. + ADD HL,DE + LD B,H + LD C,L + CALL SETTRK ;select this track. + POP DE ;reset current track pointer. + LD HL,(SCRATCH2) + LD (HL),E + INC HL + LD (HL),D + POP DE + LD HL,(SCRATCH3) ;reset the first sector on this track. + LD (HL),E + INC HL + LD (HL),D + POP BC + LD A,C ;now subtract the desired one. + SUB E ;to make it relative (1-# sectors/track). + LD C,A + LD A,B + SBC A,D + LD B,A + LD HL,(XLATE) ;translate this sector according to this table. + EX DE,HL + CALL SECTRN ;let the bios translate it. + LD C,L + LD B,H + JP SETSEC ;and select it. +; +; Compute block number from record number (SAVNREC) and +; extent number (SAVEXT). +; +GETBLOCK: LD HL,BLKSHFT ;get logical to physical conversion. + LD C,(HL) ;note that this is base 2 log of ratio. + LD A,(SAVNREC) ;get record number. +GETBLK1:OR A ;compute (A)=(A)/2^BLKSHFT. + RRA + DEC C + JP NZ,GETBLK1 + LD B,A ;save result in (B). + LD A,8 + SUB (HL) + LD C,A ;compute (C)=8-BLKSHFT. + LD A,(SAVEXT) +GETBLK2:DEC C ;compute (A)=SAVEXT*2^(8-BLKSHFT). + JP Z,GETBLK3 + OR A + RLA + JP GETBLK2 +GETBLK3:ADD A,B + RET +; +; Routine to extract the (BC) block byte from the fcb pointed +; to by (PARAMS). If this is a big-disk, then these are 16 bit +; block numbers, else they are 8 bit numbers. +; Number is returned in (HL). +; +EXTBLK: LD HL,(PARAMS) ;get fcb address. + LD DE,16 ;block numbers start 16 bytes into fcb. + ADD HL,DE + ADD HL,BC + LD A,(BIGDISK) ;are we using a big-disk? + OR A + JP Z,EXTBLK1 + LD L,(HL) ;no, extract an 8 bit number from the fcb. + LD H,0 + RET +EXTBLK1:ADD HL,BC ;yes, extract a 16 bit number. + LD E,(HL) + INC HL + LD D,(HL) + EX DE,HL ;return in (HL). + RET +; +; Compute block number. +; +COMBLK: CALL GETBLOCK + LD C,A + LD B,0 + CALL EXTBLK + LD (BLKNMBR),HL + RET +; +; Check for a zero block number (unused). +; +CHKBLK: LD HL,(BLKNMBR) + LD A,L ;is it zero? + OR H + RET +; +; Adjust physical block (BLKNMBR) and convert to logical +; sector (LOGSECT). This is the starting sector of this block. +; The actual sector of interest is then added to this and the +; resulting sector number is stored back in (BLKNMBR). This +; will still have to be adjusted for the track number. +; +LOGICAL:LD A,(BLKSHFT) ;get log2(physical/logical sectors). + LD HL,(BLKNMBR) ;get physical sector desired. +LOGICL1:ADD HL,HL ;compute logical sector number. + DEC A ;note logical sectors are 128 bytes long. + JP NZ,LOGICL1 + LD (LOGSECT),HL ;save logical sector. + LD A,(BLKMASK) ;get block mask. + LD C,A + LD A,(SAVNREC) ;get next sector to access. + AND C ;extract the relative position within physical block. + OR L ;and add it too logical sector. + LD L,A + LD (BLKNMBR),HL ;and store. + RET +; +; Set (HL) to point to extent byte in fcb. +; +SETEXT: LD HL,(PARAMS) + LD DE,12 ;it is the twelth byte. + ADD HL,DE + RET +; +; Set (HL) to point to record count byte in fcb and (DE) to +; next record number byte. +; +SETHLDE:LD HL,(PARAMS) + LD DE,15 ;record count byte (#15). + ADD HL,DE + EX DE,HL + LD HL,17 ;next record number (#32). + ADD HL,DE + RET +; +; Save current file data from fcb. +; +STRDATA:CALL SETHLDE + LD A,(HL) ;get and store record count byte. + LD (SAVNREC),A + EX DE,HL + LD A,(HL) ;get and store next record number byte. + LD (SAVNXT),A + CALL SETEXT ;point to extent byte. + LD A,(EXTMASK) ;get extent mask. + AND (HL) + LD (SAVEXT),A ;and save extent here. + RET +; +; Set the next record to access. If (MODE) is set to 2, then +; the last record byte (SAVNREC) has the correct number to access. +; For sequential access, (MODE) will be equal to 1. +; +SETNREC:CALL SETHLDE + LD A,(MODE) ;get sequential flag (=1). + CP 2 ;a 2 indicates that no adder is needed. + JP NZ,STNREC1 + XOR A ;clear adder (random access?). +STNREC1:LD C,A + LD A,(SAVNREC) ;get last record number. + ADD A,C ;increment record count. + LD (HL),A ;and set fcb's next record byte. + EX DE,HL + LD A,(SAVNXT) ;get next record byte from storage. + LD (HL),A ;and put this into fcb as number of records used. + RET +; +; Shift (HL) right (C) bits. +; +SHIFTR: INC C +SHIFTR1:DEC C + RET Z + LD A,H + OR A + RRA + LD H,A + LD A,L + RRA + LD L,A + JP SHIFTR1 +; +; Compute the check-sum for the directory buffer. Return +; integer sum in (A). +; +CHECKSUM: LD C,128 ;length of buffer. + LD HL,(DIRBUF) ;get its location. + XOR A ;clear summation byte. +CHKSUM1:ADD A,(HL) ;and compute sum ignoring carries. + INC HL + DEC C + JP NZ,CHKSUM1 + RET +; +; Shift (HL) left (C) bits. +; +SHIFTL: INC C +SHIFTL1:DEC C + RET Z + ADD HL,HL ;shift left 1 bit. + JP SHIFTL1 +; +; Routine to set a bit in a 16 bit value contained in (BC). +; The bit set depends on the current drive selection. +; +SETBIT: PUSH BC ;save 16 bit word. + LD A,(ACTIVE) ;get active drive. + LD C,A + LD HL,1 + CALL SHIFTL ;shift bit 0 into place. + POP BC ;now 'or' this with the original word. + LD A,C + OR L + LD L,A ;low byte done, do high byte. + LD A,B + OR H + LD H,A + RET +; +; Extract the write protect status bit for the current drive. +; The result is returned in (A), bit 0. +; +GETWPRT:LD HL,(WRTPRT) ;get status bytes. + LD A,(ACTIVE) ;which drive is current? + LD C,A + CALL SHIFTR ;shift status such that bit 0 is the + LD A,L ;one of interest for this drive. + AND 01H ;and isolate it. + RET +; +; Function to write protect the current disk. +; +WRTPRTD:LD HL,WRTPRT ;point to status word. + LD C,(HL) ;set (BC) equal to the status. + INC HL + LD B,(HL) + CALL SETBIT ;and set this bit according to current drive. + LD (WRTPRT),HL ;then save. + LD HL,(DIRSIZE) ;now save directory size limit. + INC HL ;remember the last one. + EX DE,HL + LD HL,(SCRATCH1) ;and store it here. + LD (HL),E ;put low byte. + INC HL + LD (HL),D ;then high byte. + RET +; +; Check for a read only file. +; +CHKROFL:CALL FCB2HL ;set (HL) to file entry in directory buffer. +CKROF1: LD DE,9 ;look at bit 7 of the ninth byte. + ADD HL,DE + LD A,(HL) + RLA + RET NC ;return if ok. + LD HL,ROFILE ;else, print error message and terminate. + JP JUMPHL +; +; Check the write protect status of the active disk. +; +CHKWPRT:CALL GETWPRT + RET Z ;return if ok. + LD HL,RODISK ;else print message and terminate. + JP JUMPHL +; +; Routine to set (HL) pointing to the proper entry in the +; directory buffer. +; +FCB2HL: LD HL,(DIRBUF) ;get address of buffer. + LD A,(FCBPOS) ;relative position of file. +; +; Routine to add (A) to (HL). +; +ADDA2HL:ADD A,L + LD L,A + RET NC + INC H ;take care of any carry. + RET +; +; Routine to get the 's2' byte from the fcb supplied in +; the initial parameter specification. +; +GETS2: LD HL,(PARAMS) ;get address of fcb. + LD DE,14 ;relative position of 's2'. + ADD HL,DE + LD A,(HL) ;extract this byte. + RET +; +; Clear the 's2' byte in the fcb. +; +CLEARS2:CALL GETS2 ;this sets (HL) pointing to it. + LD (HL),0 ;now clear it. + RET +; +; Set bit 7 in the 's2' byte of the fcb. +; +SETS2B7:CALL GETS2 ;get the byte. + OR 80H ;and set bit 7. + LD (HL),A ;then store. + RET +; +; Compare (FILEPOS) with (SCRATCH1) and set flags based on +; the difference. This checks to see if there are more file +; names in the directory. We are at (FILEPOS) and there are +; (SCRATCH1) of them to check. +; +MOREFLS:LD HL,(FILEPOS) ;we are here. + EX DE,HL + LD HL,(SCRATCH1) ;and don't go past here. + LD A,E ;compute difference but don't keep. + SUB (HL) + INC HL + LD A,D + SBC A,(HL) ;set carry if no more names. + RET +; +; Call this routine to prevent (SCRATCH1) from being greater +; than (FILEPOS). +; +CHKNMBR:CALL MOREFLS ;SCRATCH1 too big? + RET C + INC DE ;yes, reset it to (FILEPOS). + LD (HL),D + DEC HL + LD (HL),E + RET +; +; Compute (HL)=(DE)-(HL) +; +SUBHL: LD A,E ;compute difference. + SUB L + LD L,A ;store low byte. + LD A,D + SBC A,H + LD H,A ;and then high byte. + RET +; +; Set the directory checksum byte. +; +SETDIR: LD C,0FFH +; +; Routine to set or compare the directory checksum byte. If +; (C)=0ffh, then this will set the checksum byte. Else the byte +; will be checked. If the check fails (the disk has been changed), +; then this disk will be write protected. +; +CHECKDIR: LD HL,(CKSUMTBL) + EX DE,HL + LD HL,(ALLOC1) + CALL SUBHL + RET NC ;ok if (CKSUMTBL) > (ALLOC1), so return. + PUSH BC + CALL CHECKSUM ;else compute checksum. + LD HL,(CHKVECT) ;get address of checksum table. + EX DE,HL + LD HL,(CKSUMTBL) + ADD HL,DE ;set (HL) to point to byte for this drive. + POP BC + INC C ;set or check ? + JP Z,CHKDIR1 + CP (HL) ;check them. + RET Z ;return if they are the same. + CALL MOREFLS ;not the same, do we care? + RET NC + CALL WRTPRTD ;yes, mark this as write protected. + RET +CHKDIR1:LD (HL),A ;just set the byte. + RET +; +; Do a write to the directory of the current disk. +; +DIRWRITE: CALL SETDIR ;set checksum byte. + CALL DIRDMA ;set directory dma address. + LD C,1 ;tell the bios to actually write. + CALL DOWRITE ;then do the write. + JP DEFDMA +; +; Read from the directory. +; +DIRREAD:CALL DIRDMA ;set the directory dma address. + CALL DOREAD ;and read it. +; +; Routine to set the dma address to the users choice. +; +DEFDMA: LD HL,USERDMA ;reset the default dma address and return. + JP DIRDMA1 +; +; Routine to set the dma address for directory work. +; +DIRDMA: LD HL,DIRBUF +; +; Set the dma address. On entry, (HL) points to +; word containing the desired dma address. +; +DIRDMA1:LD C,(HL) + INC HL + LD B,(HL) ;setup (BC) and go to the bios to set it. + JP SETDMA +; +; Move the directory buffer into user's dma space. +; +MOVEDIR:LD HL,(DIRBUF) ;buffer is located here, and + EX DE,HL + LD HL,(USERDMA) ; put it here. + LD C,128 ;this is its length. + JP DE2HL ;move it now and return. +; +; Check (FILEPOS) and set the zero flag if it equals 0ffffh. +; +CKFILPOS: LD HL,FILEPOS + LD A,(HL) + INC HL + CP (HL) ;are both bytes the same? + RET NZ + INC A ;yes, but are they each 0ffh? + RET +; +; Set location (FILEPOS) to 0ffffh. +; +STFILPOS: LD HL,0FFFFH + LD (FILEPOS),HL + RET +; +; Move on to the next file position within the current +; directory buffer. If no more exist, set pointer to 0ffffh +; and the calling routine will check for this. Enter with (C) +; equal to 0ffh to cause the checksum byte to be set, else we +; will check this disk and set write protect if checksums are +; not the same (applies only if another directory sector must +; be read). +; +NXENTRY:LD HL,(DIRSIZE) ;get directory entry size limit. + EX DE,HL + LD HL,(FILEPOS) ;get current count. + INC HL ;go on to the next one. + LD (FILEPOS),HL + CALL SUBHL ;(HL)=(DIRSIZE)-(FILEPOS) + JP NC,NXENT1 ;is there more room left? + JP STFILPOS ;no. Set this flag and return. +NXENT1: LD A,(FILEPOS) ;get file position within directory. + AND 03H ;only look within this sector (only 4 entries fit). + LD B,5 ;convert to relative position (32 bytes each). +NXENT2: ADD A,A ;note that this is not efficient code. + DEC B ;5 'ADD A's would be better. + JP NZ,NXENT2 + LD (FCBPOS),A ;save it as position of fcb. + OR A + RET NZ ;return if we are within buffer. + PUSH BC + CALL TRKSEC ;we need the next directory sector. + CALL DIRREAD + POP BC + JP CHECKDIR +; +; Routine to to get a bit from the disk space allocation +; map. It is returned in (A), bit position 0. On entry to here, +; set (BC) to the block number on the disk to check. +; On return, (D) will contain the original bit position for +; this block number and (HL) will point to the address for it. +; +CKBITMAP: LD A,C ;determine bit number of interest. + AND 07H ;compute (D)=(E)=(C and 7)+1. + INC A + LD E,A ;save particular bit number. + LD D,A +; +; compute (BC)=(BC)/8. +; + LD A,C + RRCA ;now shift right 3 bits. + RRCA + RRCA + AND 1FH ;and clear bits 7,6,5. + LD C,A + LD A,B + ADD A,A ;now shift (B) into bits 7,6,5. + ADD A,A + ADD A,A + ADD A,A + ADD A,A + OR C ;and add in (C). + LD C,A ;ok, (C) ha been completed. + LD A,B ;is there a better way of doing this? + RRCA + RRCA + RRCA + AND 1FH + LD B,A ;and now (B) is completed. +; +; use this as an offset into the disk space allocation +; table. +; + LD HL,(ALOCVECT) + ADD HL,BC + LD A,(HL) ;now get correct byte. +CKBMAP1:RLCA ;get correct bit into position 0. + DEC E + JP NZ,CKBMAP1 + RET +; +; Set or clear the bit map such that block number (BC) will be marked +; as used. On entry, if (E)=0 then this bit will be cleared, if it equals +; 1 then it will be set (don't use anyother values). +; +STBITMAP: PUSH DE + CALL CKBITMAP ;get the byte of interest. + AND 0FEH ;clear the affected bit. + POP BC + OR C ;and now set it acording to (C). +; +; entry to restore the original bit position and then store +; in table. (A) contains the value, (D) contains the bit +; position (1-8), and (HL) points to the address within the +; space allocation table for this byte. +; +STBMAP1:RRCA ;restore original bit position. + DEC D + JP NZ,STBMAP1 + LD (HL),A ;and stor byte in table. + RET +; +; Set/clear space used bits in allocation map for this file. +; On entry, (C)=1 to set the map and (C)=0 to clear it. +; +SETFILE:CALL FCB2HL ;get address of fcb + LD DE,16 + ADD HL,DE ;get to block number bytes. + PUSH BC + LD C,17 ;check all 17 bytes (max) of table. +SETFL1: POP DE + DEC C ;done all bytes yet? + RET Z + PUSH DE + LD A,(BIGDISK) ;check disk size for 16 bit block numbers. + OR A + JP Z,SETFL2 + PUSH BC ;only 8 bit numbers. set (BC) to this one. + PUSH HL + LD C,(HL) ;get low byte from table, always + LD B,0 ;set high byte to zero. + JP SETFL3 +SETFL2: DEC C ;for 16 bit block numbers, adjust counter. + PUSH BC + LD C,(HL) ;now get both the low and high bytes. + INC HL + LD B,(HL) + PUSH HL +SETFL3: LD A,C ;block used? + OR B + JP Z,SETFL4 + LD HL,(DSKSIZE) ;is this block number within the + LD A,L ;space on the disk? + SUB C + LD A,H + SBC A,B + CALL NC,STBITMAP ;yes, set the proper bit. +SETFL4: POP HL ;point to next block number in fcb. + INC HL + POP BC + JP SETFL1 +; +; Construct the space used allocation bit map for the active +; drive. If a file name starts with '$' and it is under the +; current user number, then (STATUS) is set to minus 1. Otherwise +; it is not set at all. +; +BITMAP: LD HL,(DSKSIZE) ;compute size of allocation table. + LD C,3 + CALL SHIFTR ;(HL)=(HL)/8. + INC HL ;at lease 1 byte. + LD B,H + LD C,L ;set (BC) to the allocation table length. +; +; Initialize the bitmap for this drive. Right now, the first +; two bytes are specified by the disk parameter block. However +; a patch could be entered here if it were necessary to setup +; this table in a special mannor. For example, the bios could +; determine locations of 'bad blocks' and set them as already +; 'used' in the map. +; + LD HL,(ALOCVECT) ;now zero out the table now. +BITMAP1:LD (HL),0 + INC HL + DEC BC + LD A,B + OR C + JP NZ,BITMAP1 + LD HL,(ALLOC0) ;get initial space used by directory. + EX DE,HL + LD HL,(ALOCVECT) ;and put this into map. + LD (HL),E + INC HL + LD (HL),D +; +; End of initialization portion. +; + CALL HOMEDRV ;now home the drive. + LD HL,(SCRATCH1) + LD (HL),3 ;force next directory request to read + INC HL ;in a sector. + LD (HL),0 + CALL STFILPOS ;clear initial file position also. +BITMAP2:LD C,0FFH ;read next file name in directory + CALL NXENTRY ;and set checksum byte. + CALL CKFILPOS ;is there another file? + RET Z + CALL FCB2HL ;yes, get its address. + LD A,0E5H + CP (HL) ;empty file entry? + JP Z,BITMAP2 + LD A,(USERNO) ;no, correct user number? + CP (HL) + JP NZ,BITMAP3 + INC HL + LD A,(HL) ;yes, does name start with a '$'? + SUB '$' + JP NZ,BITMAP3 + DEC A ;yes, set atatus to minus one. + LD (STATUS),A +BITMAP3:LD C,1 ;now set this file's space as used in bit map. + CALL SETFILE + CALL CHKNMBR ;keep (SCRATCH1) in bounds. + JP BITMAP2 +; +; Set the status (STATUS) and return. +; +STSTATUS: LD A,(FNDSTAT) + JP SETSTAT +; +; Check extents in (A) and (C). Set the zero flag if they +; are the same. The number of 16k chunks of disk space that +; the directory extent covers is expressad is (EXTMASK+1). +; No registers are modified. +; +SAMEXT: PUSH BC + PUSH AF + LD A,(EXTMASK) ;get extent mask and use it to + CPL ;to compare both extent numbers. + LD B,A ;save resulting mask here. + LD A,C ;mask first extent and save in (C). + AND B + LD C,A + POP AF ;now mask second extent and compare + AND B ;with the first one. + SUB C + AND 1FH ;(* only check buts 0-4 *) + POP BC ;the zero flag is set if they are the same. + RET ;restore (BC) and return. +; +; Search for the first occurence of a file name. On entry, +; register (C) should contain the number of bytes of the fcb +; that must match. +; +FINDFST:LD A,0FFH + LD (FNDSTAT),A + LD HL,COUNTER ;save character count. + LD (HL),C + LD HL,(PARAMS) ;get filename to match. + LD (SAVEFCB),HL ;and save. + CALL STFILPOS ;clear initial file position (set to 0ffffh). + CALL HOMEDRV ;home the drive. +; +; Entry to locate the next occurence of a filename within the +; directory. The disk is not expected to have been changed. If +; it was, then it will be write protected. +; +FINDNXT:LD C,0 ;write protect the disk if changed. + CALL NXENTRY ;get next filename entry in directory. + CALL CKFILPOS ;is file position = 0ffffh? + JP Z,FNDNXT6 ;yes, exit now then. + LD HL,(SAVEFCB) ;set (DE) pointing to filename to match. + EX DE,HL + LD A,(DE) + CP 0E5H ;empty directory entry? + JP Z,FNDNXT1 ;(* are we trying to reserect erased entries? *) + PUSH DE + CALL MOREFLS ;more files in directory? + POP DE + JP NC,FNDNXT6 ;no more. Exit now. +FNDNXT1:CALL FCB2HL ;get address of this fcb in directory. + LD A,(COUNTER) ;get number of bytes (characters) to check. + LD C,A + LD B,0 ;initialize byte position counter. +FNDNXT2:LD A,C ;are we done with the compare? + OR A + JP Z,FNDNXT5 + LD A,(DE) ;no, check next byte. + CP '?' ;don't care about this character? + JP Z,FNDNXT4 + LD A,B ;get bytes position in fcb. + CP 13 ;don't care about the thirteenth byte either. + JP Z,FNDNXT4 + CP 12 ;extent byte? + LD A,(DE) + JP Z,FNDNXT3 + SUB (HL) ;otherwise compare characters. + AND 7FH + JP NZ,FINDNXT ;not the same, check next entry. + JP FNDNXT4 ;so far so good, keep checking. +FNDNXT3:PUSH BC ;check the extent byte here. + LD C,(HL) + CALL SAMEXT + POP BC + JP NZ,FINDNXT ;not the same, look some more. +; +; So far the names compare. Bump pointers to the next byte +; and continue until all (C) characters have been checked. +; +FNDNXT4:INC DE ;bump pointers. + INC HL + INC B + DEC C ;adjust character counter. + JP FNDNXT2 +FNDNXT5:LD A,(FILEPOS) ;return the position of this entry. + AND 03H + LD (STATUS),A + LD HL,FNDSTAT + LD A,(HL) + RLA + RET NC + XOR A + LD (HL),A + RET +; +; Filename was not found. Set appropriate status. +; +FNDNXT6:CALL STFILPOS ;set (FILEPOS) to 0ffffh. + LD A,0FFH ;say not located. + JP SETSTAT +; +; Erase files from the directory. Only the first byte of the +; fcb will be affected. It is set to (E5). +; +ERAFILE:CALL CHKWPRT ;is disk write protected? + LD C,12 ;only compare file names. + CALL FINDFST ;get first file name. +ERAFIL1:CALL CKFILPOS ;any found? + RET Z ;nope, we must be done. + CALL CHKROFL ;is file read only? + CALL FCB2HL ;nope, get address of fcb and + LD (HL),0E5H ;set first byte to 'empty'. + LD C,0 ;clear the space from the bit map. + CALL SETFILE + CALL DIRWRITE ;now write the directory sector back out. + CALL FINDNXT ;find the next file name. + JP ERAFIL1 ;and repeat process. +; +; Look through the space allocation map (bit map) for the +; next available block. Start searching at block number (BC-1). +; The search procedure is to look for an empty block that is +; before the starting block. If not empty, look at a later +; block number. In this way, we return the closest empty block +; on either side of the 'target' block number. This will speed +; access on random devices. For serial devices, this should be +; changed to look in the forward direction first and then start +; at the front and search some more. +; +; On return, (DE)= block number that is empty and (HL) =0 +; if no empry block was found. +; +FNDSPACE: LD D,B ;set (DE) as the block that is checked. + LD E,C +; +; Look before target block. Registers (BC) are used as the lower +; pointer and (DE) as the upper pointer. +; +FNDSPA1:LD A,C ;is block 0 specified? + OR B + JP Z,FNDSPA2 + DEC BC ;nope, check previous block. + PUSH DE + PUSH BC + CALL CKBITMAP + RRA ;is this block empty? + JP NC,FNDSPA3 ;yes. use this. +; +; Note that the above logic gets the first block that it finds +; that is empty. Thus a file could be written 'backward' making +; it very slow to access. This could be changed to look for the +; first empty block and then continue until the start of this +; empty space is located and then used that starting block. +; This should help speed up access to some files especially on +; a well used disk with lots of fairly small 'holes'. +; + POP BC ;nope, check some more. + POP DE +; +; Now look after target block. +; +FNDSPA2:LD HL,(DSKSIZE) ;is block (DE) within disk limits? + LD A,E + SUB L + LD A,D + SBC A,H + JP NC,FNDSPA4 + INC DE ;yes, move on to next one. + PUSH BC + PUSH DE + LD B,D + LD C,E + CALL CKBITMAP ;check it. + RRA ;empty? + JP NC,FNDSPA3 + POP DE ;nope, continue searching. + POP BC + JP FNDSPA1 +; +; Empty block found. Set it as used and return with (HL) +; pointing to it (true?). +; +FNDSPA3:RLA ;reset byte. + INC A ;and set bit 0. + CALL STBMAP1 ;update bit map. + POP HL ;set return registers. + POP DE + RET +; +; Free block was not found. If (BC) is not zero, then we have +; not checked all of the disk space. +; +FNDSPA4:LD A,C + OR B + JP NZ,FNDSPA1 + LD HL,0 ;set 'not found' status. + RET +; +; Move a complete fcb entry into the directory and write it. +; +FCBSET: LD C,0 + LD E,32 ;length of each entry. +; +; Move (E) bytes from the fcb pointed to by (PARAMS) into +; fcb in directory starting at relative byte (C). This updated +; directory buffer is then written to the disk. +; +UPDATE: PUSH DE + LD B,0 ;set (BC) to relative byte position. + LD HL,(PARAMS) ;get address of fcb. + ADD HL,BC ;compute starting byte. + EX DE,HL + CALL FCB2HL ;get address of fcb to update in directory. + POP BC ;set (C) to number of bytes to change. + CALL DE2HL +UPDATE1:CALL TRKSEC ;determine the track and sector affected. + JP DIRWRITE ;then write this sector out. +; +; Routine to change the name of all files on the disk with a +; specified name. The fcb contains the current name as the +; first 12 characters and the new name 16 bytes into the fcb. +; +CHGNAMES: CALL CHKWPRT ;check for a write protected disk. + LD C,12 ;match first 12 bytes of fcb only. + CALL FINDFST ;get first name. + LD HL,(PARAMS) ;get address of fcb. + LD A,(HL) ;get user number. + LD DE,16 ;move over to desired name. + ADD HL,DE + LD (HL),A ;keep same user number. +CHGNAM1:CALL CKFILPOS ;any matching file found? + RET Z ;no, we must be done. + CALL CHKROFL ;check for read only file. + LD C,16 ;start 16 bytes into fcb. + LD E,12 ;and update the first 12 bytes of directory. + CALL UPDATE + CALL FINDNXT ;get te next file name. + JP CHGNAM1 ;and continue. +; +; Update a files attributes. The procedure is to search for +; every file with the same name as shown in fcb (ignoring bit 7) +; and then to update it (which includes bit 7). No other changes +; are made. +; +SAVEATTR: LD C,12 ;match first 12 bytes. + CALL FINDFST ;look for first filename. +SAVATR1:CALL CKFILPOS ;was one found? + RET Z ;nope, we must be done. + LD C,0 ;yes, update the first 12 bytes now. + LD E,12 + CALL UPDATE ;update filename and write directory. + CALL FINDNXT ;and get the next file. + JP SAVATR1 ;then continue until done. +; +; Open a file (name specified in fcb). +; +OPENIT: LD C,15 ;compare the first 15 bytes. + CALL FINDFST ;get the first one in directory. + CALL CKFILPOS ;any at all? + RET Z +OPENIT1:CALL SETEXT ;point to extent byte within users fcb. + LD A,(HL) ;and get it. + PUSH AF ;save it and address. + PUSH HL + CALL FCB2HL ;point to fcb in directory. + EX DE,HL + LD HL,(PARAMS) ;this is the users copy. + LD C,32 ;move it into users space. + PUSH DE + CALL DE2HL + CALL SETS2B7 ;set bit 7 in 's2' byte (unmodified). + POP DE ;now get the extent byte from this fcb. + LD HL,12 + ADD HL,DE + LD C,(HL) ;into (C). + LD HL,15 ;now get the record count byte into (B). + ADD HL,DE + LD B,(HL) + POP HL ;keep the same extent as the user had originally. + POP AF + LD (HL),A + LD A,C ;is it the same as in the directory fcb? + CP (HL) + LD A,B ;if yes, then use the same record count. + JP Z,OPENIT2 + LD A,0 ;if the user specified an extent greater than + JP C,OPENIT2 ;the one in the directory, then set record count to 0. + LD A,128 ;otherwise set to maximum. +OPENIT2:LD HL,(PARAMS) ;set record count in users fcb to (A). + LD DE,15 + ADD HL,DE ;compute relative position. + LD (HL),A ;and set the record count. + RET +; +; Move two bytes from (DE) to (HL) if (and only if) (HL) +; point to a zero value (16 bit). +; Return with zero flag set it (DE) was moved. Registers (DE) +; and (HL) are not changed. However (A) is. +; +MOVEWORD: LD A,(HL) ;check for a zero word. + INC HL + OR (HL) ;both bytes zero? + DEC HL + RET NZ ;nope, just return. + LD A,(DE) ;yes, move two bytes from (DE) into + LD (HL),A ;this zero space. + INC DE + INC HL + LD A,(DE) + LD (HL),A + DEC DE ;don't disturb these registers. + DEC HL + RET +; +; Get here to close a file specified by (fcb). +; +CLOSEIT:XOR A ;clear status and file position bytes. + LD (STATUS),A + LD (FILEPOS),A + LD (FILEPOS+1),A + CALL GETWPRT ;get write protect bit for this drive. + RET NZ ;just return if it is set. + CALL GETS2 ;else get the 's2' byte. + AND 80H ;and look at bit 7 (file unmodified?). + RET NZ ;just return if set. + LD C,15 ;else look up this file in directory. + CALL FINDFST + CALL CKFILPOS ;was it found? + RET Z ;just return if not. + LD BC,16 ;set (HL) pointing to records used section. + CALL FCB2HL + ADD HL,BC + EX DE,HL + LD HL,(PARAMS) ;do the same for users specified fcb. + ADD HL,BC + LD C,16 ;this many bytes are present in this extent. +CLOSEIT1: LD A,(BIGDISK) ;8 or 16 bit record numbers? + OR A + JP Z,CLOSEIT4 + LD A,(HL) ;just 8 bit. Get one from users fcb. + OR A + LD A,(DE) ;now get one from directory fcb. + JP NZ,CLOSEIT2 + LD (HL),A ;users byte was zero. Update from directory. +CLOSEIT2: OR A + JP NZ,CLOSEIT3 + LD A,(HL) ;directories byte was zero, update from users fcb. + LD (DE),A +CLOSEIT3: CP (HL) ;if neither one of these bytes were zero, + JP NZ,CLOSEIT7 ;then close error if they are not the same. + JP CLOSEIT5 ;ok so far, get to next byte in fcbs. +CLOSEIT4: CALL MOVEWORD ;update users fcb if it is zero. + EX DE,HL + CALL MOVEWORD ;update directories fcb if it is zero. + EX DE,HL + LD A,(DE) ;if these two values are no different, + CP (HL) ;then a close error occured. + JP NZ,CLOSEIT7 + INC DE ;check second byte. + INC HL + LD A,(DE) + CP (HL) + JP NZ,CLOSEIT7 + DEC C ;remember 16 bit values. +CLOSEIT5: INC DE ;bump to next item in table. + INC HL + DEC C ;there are 16 entries only. + JP NZ,CLOSEIT1 ;continue if more to do. + LD BC,0FFECH ;backup 20 places (extent byte). + ADD HL,BC + EX DE,HL + ADD HL,BC + LD A,(DE) + CP (HL) ;directory's extent already greater than the + JP C,CLOSEIT6 ;users extent? + LD (HL),A ;no, update directory extent. + LD BC,3 ;and update the record count byte in + ADD HL,BC ;directories fcb. + EX DE,HL + ADD HL,BC + LD A,(HL) ;get from user. + LD (DE),A ;and put in directory. +CLOSEIT6: LD A,0FFH ;set 'was open and is now closed' byte. + LD (CLOSEFLG),A + JP UPDATE1 ;update the directory now. +CLOSEIT7: LD HL,STATUS ;set return status and then return. + DEC (HL) + RET +; +; Routine to get the next empty space in the directory. It +; will then be cleared for use. +; +GETEMPTY: CALL CHKWPRT ;make sure disk is not write protected. + LD HL,(PARAMS) ;save current parameters (fcb). + PUSH HL + LD HL,EMPTYFCB ;use special one for empty space. + LD (PARAMS),HL + LD C,1 ;search for first empty spot in directory. + CALL FINDFST ;(* only check first byte *) + CALL CKFILPOS ;none? + POP HL + LD (PARAMS),HL ;restore original fcb address. + RET Z ;return if no more space. + EX DE,HL + LD HL,15 ;point to number of records for this file. + ADD HL,DE + LD C,17 ;and clear all of this space. + XOR A +GETMT1: LD (HL),A + INC HL + DEC C + JP NZ,GETMT1 + LD HL,13 ;clear the 's1' byte also. + ADD HL,DE + LD (HL),A + CALL CHKNMBR ;keep (SCRATCH1) within bounds. + CALL FCBSET ;write out this fcb entry to directory. + JP SETS2B7 ;set 's2' byte bit 7 (unmodified at present). +; +; Routine to close the current extent and open the next one +; for reading. +; +GETNEXT:XOR A + LD (CLOSEFLG),A ;clear close flag. + CALL CLOSEIT ;close this extent. + CALL CKFILPOS + RET Z ;not there??? + LD HL,(PARAMS) ;get extent byte. + LD BC,12 + ADD HL,BC + LD A,(HL) ;and increment it. + INC A + AND 1FH ;keep within range 0-31. + LD (HL),A + JP Z,GTNEXT1 ;overflow? + LD B,A ;mask extent byte. + LD A,(EXTMASK) + AND B + LD HL,CLOSEFLG ;check close flag (0ffh is ok). + AND (HL) + JP Z,GTNEXT2 ;if zero, we must read in next extent. + JP GTNEXT3 ;else, it is already in memory. +GTNEXT1:LD BC,2 ;Point to the 's2' byte. + ADD HL,BC + INC (HL) ;and bump it. + LD A,(HL) ;too many extents? + AND 0FH + JP Z,GTNEXT5 ;yes, set error code. +; +; Get here to open the next extent. +; +GTNEXT2:LD C,15 ;set to check first 15 bytes of fcb. + CALL FINDFST ;find the first one. + CALL CKFILPOS ;none available? + JP NZ,GTNEXT3 + LD A,(RDWRTFLG) ;no extent present. Can we open an empty one? + INC A ;0ffh means reading (so not possible). + JP Z,GTNEXT5 ;or an error. + CALL GETEMPTY ;we are writing, get an empty entry. + CALL CKFILPOS ;none? + JP Z,GTNEXT5 ;error if true. + JP GTNEXT4 ;else we are almost done. +GTNEXT3:CALL OPENIT1 ;open this extent. +GTNEXT4:CALL STRDATA ;move in updated data (rec #, extent #, etc.) + XOR A ;clear status and return. + JP SETSTAT +; +; Error in extending the file. Too many extents were needed +; or not enough space on the disk. +; +GTNEXT5:CALL IOERR1 ;set error code, clear bit 7 of 's2' + JP SETS2B7 ;so this is not written on a close. +; +; Read a sequential file. +; +RDSEQ: LD A,1 ;set sequential access mode. + LD (MODE),A +RDSEQ1: LD A,0FFH ;don't allow reading unwritten space. + LD (RDWRTFLG),A + CALL STRDATA ;put rec# and ext# into fcb. + LD A,(SAVNREC) ;get next record to read. + LD HL,SAVNXT ;get number of records in extent. + CP (HL) ;within this extent? + JP C,RDSEQ2 + CP 128 ;no. Is this extent fully used? + JP NZ,RDSEQ3 ;no. End-of-file. + CALL GETNEXT ;yes, open the next one. + XOR A ;reset next record to read. + LD (SAVNREC),A + LD A,(STATUS) ;check on open, successful? + OR A + JP NZ,RDSEQ3 ;no, error. +RDSEQ2: CALL COMBLK ;ok. compute block number to read. + CALL CHKBLK ;check it. Within bounds? + JP Z,RDSEQ3 ;no, error. + CALL LOGICAL ;convert (BLKNMBR) to logical sector (128 byte). + CALL TRKSEC1 ;set the track and sector for this block #. + CALL DOREAD ;and read it. + JP SETNREC ;and set the next record to be accessed. +; +; Read error occured. Set status and return. +; +RDSEQ3: JP IOERR1 +; +; Write the next sequential record. +; +WTSEQ: LD A,1 ;set sequential access mode. + LD (MODE),A +WTSEQ1: LD A,0 ;allow an addition empty extent to be opened. + LD (RDWRTFLG),A + CALL CHKWPRT ;check write protect status. + LD HL,(PARAMS) + CALL CKROF1 ;check for read only file, (HL) already set to fcb. + CALL STRDATA ;put updated data into fcb. + LD A,(SAVNREC) ;get record number to write. + CP 128 ;within range? + JP NC,IOERR1 ;no, error(?). + CALL COMBLK ;compute block number. + CALL CHKBLK ;check number. + LD C,0 ;is there one to write to? + JP NZ,WTSEQ6 ;yes, go do it. + CALL GETBLOCK ;get next block number within fcb to use. + LD (RELBLOCK),A ;and save. + LD BC,0 ;start looking for space from the start + OR A ;if none allocated as yet. + JP Z,WTSEQ2 + LD C,A ;extract previous block number from fcb + DEC BC ;so we can be closest to it. + CALL EXTBLK + LD B,H + LD C,L +WTSEQ2: CALL FNDSPACE ;find the next empty block nearest number (BC). + LD A,L ;check for a zero number. + OR H + JP NZ,WTSEQ3 + LD A,2 ;no more space? + JP SETSTAT +WTSEQ3: LD (BLKNMBR),HL ;save block number to access. + EX DE,HL ;put block number into (DE). + LD HL,(PARAMS) ;now we must update the fcb for this + LD BC,16 ;newly allocated block. + ADD HL,BC + LD A,(BIGDISK) ;8 or 16 bit block numbers? + OR A + LD A,(RELBLOCK) ;(* update this entry *) + JP Z,WTSEQ4 ;zero means 16 bit ones. + CALL ADDA2HL ;(HL)=(HL)+(A) + LD (HL),E ;store new block number. + JP WTSEQ5 +WTSEQ4: LD C,A ;compute spot in this 16 bit table. + LD B,0 + ADD HL,BC + ADD HL,BC + LD (HL),E ;stuff block number (DE) there. + INC HL + LD (HL),D +WTSEQ5: LD C,2 ;set (C) to indicate writing to un-used disk space. +WTSEQ6: LD A,(STATUS) ;are we ok so far? + OR A + RET NZ + PUSH BC ;yes, save write flag for bios (register C). + CALL LOGICAL ;convert (BLKNMBR) over to loical sectors. + LD A,(MODE) ;get access mode flag (1=sequential, + DEC A ;0=random, 2=special?). + DEC A + JP NZ,WTSEQ9 +; +; Special random i/o from function #40. Maybe for M/PM, but the +; current block, if it has not been written to, will be zeroed +; out and then written (reason?). +; + POP BC + PUSH BC + LD A,C ;get write status flag (2=writing unused space). + DEC A + DEC A + JP NZ,WTSEQ9 + PUSH HL + LD HL,(DIRBUF) ;zero out the directory buffer. + LD D,A ;note that (A) is zero here. +WTSEQ7: LD (HL),A + INC HL + INC D ;do 128 bytes. + JP P,WTSEQ7 + CALL DIRDMA ;tell the bios the dma address for directory access. + LD HL,(LOGSECT) ;get sector that starts current block. + LD C,2 ;set 'writing to unused space' flag. +WTSEQ8: LD (BLKNMBR),HL ;save sector to write. + PUSH BC + CALL TRKSEC1 ;determine its track and sector numbers. + POP BC + CALL DOWRITE ;now write out 128 bytes of zeros. + LD HL,(BLKNMBR) ;get sector number. + LD C,0 ;set normal write flag. + LD A,(BLKMASK) ;determine if we have written the entire + LD B,A ;physical block. + AND L + CP B + INC HL ;prepare for the next one. + JP NZ,WTSEQ8 ;continue until (BLKMASK+1) sectors written. + POP HL ;reset next sector number. + LD (BLKNMBR),HL + CALL DEFDMA ;and reset dma address. +; +; Normal disk write. Set the desired track and sector then +; do the actual write. +; +WTSEQ9: CALL TRKSEC1 ;determine track and sector for this write. + POP BC ;get write status flag. + PUSH BC + CALL DOWRITE ;and write this out. + POP BC + LD A,(SAVNREC) ;get number of records in file. + LD HL,SAVNXT ;get last record written. + CP (HL) + JP C,WTSEQ10 + LD (HL),A ;we have to update record count. + INC (HL) + LD C,2 +; +;* This area has been patched to correct disk update problem +;* when using blocking and de-blocking in the BIOS. +; +WTSEQ10:NOP ;was 'dcr c' + NOP ;was 'dcr c' + LD HL,0 ;was 'jnz wtseq99' +; +; * End of patch. +; + PUSH AF + CALL GETS2 ;set 'extent written to' flag. + AND 7FH ;(* clear bit 7 *) + LD (HL),A + POP AF ;get record count for this extent. +WTSEQ99:CP 127 ;is it full? + JP NZ,WTSEQ12 + LD A,(MODE) ;yes, are we in sequential mode? + CP 1 + JP NZ,WTSEQ12 + CALL SETNREC ;yes, set next record number. + CALL GETNEXT ;and get next empty space in directory. + LD HL,STATUS ;ok? + LD A,(HL) + OR A + JP NZ,WTSEQ11 + DEC A ;yes, set record count to -1. + LD (SAVNREC),A +WTSEQ11:LD (HL),0 ;clear status. +WTSEQ12:JP SETNREC ;set next record to access. +; +; For random i/o, set the fcb for the desired record number +; based on the 'r0,r1,r2' bytes. These bytes in the fcb are +; used as follows: +; +; fcb+35 fcb+34 fcb+33 +; | 'r-2' | 'r-1' | 'r-0' | +; |7 0 | 7 0 | 7 0| +; |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0| +; | overflow | | extra | extent | record # | +; | ______________| |_extent|__number___|_____________| +; also 's2' +; +; On entry, register (C) contains 0ffh if this is a read +; and thus we can not access unwritten disk space. Otherwise, +; another extent will be opened (for writing) if required. +; +POSITION: XOR A ;set random i/o flag. + LD (MODE),A +; +; Special entry (function #40). M/PM ? +; +POSITN1:PUSH BC ;save read/write flag. + LD HL,(PARAMS) ;get address of fcb. + EX DE,HL + LD HL,33 ;now get byte 'r0'. + ADD HL,DE + LD A,(HL) + AND 7FH ;keep bits 0-6 for the record number to access. + PUSH AF + LD A,(HL) ;now get bit 7 of 'r0' and bits 0-3 of 'r1'. + RLA + INC HL + LD A,(HL) + RLA + AND 1FH ;and save this in bits 0-4 of (C). + LD C,A ;this is the extent byte. + LD A,(HL) ;now get the extra extent byte. + RRA + RRA + RRA + RRA + AND 0FH + LD B,A ;and save it in (B). + POP AF ;get record number back to (A). + INC HL ;check overflow byte 'r2'. + LD L,(HL) + INC L + DEC L + LD L,6 ;prepare for error. + JP NZ,POSITN5 ;out of disk space error. + LD HL,32 ;store record number into fcb. + ADD HL,DE + LD (HL),A + LD HL,12 ;and now check the extent byte. + ADD HL,DE + LD A,C + SUB (HL) ;same extent as before? + JP NZ,POSITN2 + LD HL,14 ;yes, check extra extent byte 's2' also. + ADD HL,DE + LD A,B + SUB (HL) + AND 7FH + JP Z,POSITN3 ;same, we are almost done then. +; +; Get here when another extent is required. +; +POSITN2:PUSH BC + PUSH DE + CALL CLOSEIT ;close current extent. + POP DE + POP BC + LD L,3 ;prepare for error. + LD A,(STATUS) + INC A + JP Z,POSITN4 ;close error. + LD HL,12 ;put desired extent into fcb now. + ADD HL,DE + LD (HL),C + LD HL,14 ;and store extra extent byte 's2'. + ADD HL,DE + LD (HL),B + CALL OPENIT ;try and get this extent. + LD A,(STATUS) ;was it there? + INC A + JP NZ,POSITN3 + POP BC ;no. can we create a new one (writing?). + PUSH BC + LD L,4 ;prepare for error. + INC C + JP Z,POSITN4 ;nope, reading unwritten space error. + CALL GETEMPTY ;yes we can, try to find space. + LD L,5 ;prepare for error. + LD A,(STATUS) + INC A + JP Z,POSITN4 ;out of space? +; +; Normal return location. Clear error code and return. +; +POSITN3:POP BC ;restore stack. + XOR A ;and clear error code byte. + JP SETSTAT +; +; Error. Set the 's2' byte to indicate this (why?). +; +POSITN4:PUSH HL + CALL GETS2 + LD (HL),0C0H + POP HL +; +; Return with error code (presently in L). +; +POSITN5:POP BC + LD A,L ;get error code. + LD (STATUS),A + JP SETS2B7 +; +; Read a random record. +; +READRAN:LD C,0FFH ;set 'read' status. + CALL POSITION ;position the file to proper record. + CALL Z,RDSEQ1 ;and read it as usual (if no errors). + RET +; +; Write to a random record. +; +WRITERAN: LD C,0 ;set 'writing' flag. + CALL POSITION ;position the file to proper record. + CALL Z,WTSEQ1 ;and write as usual (if no errors). + RET +; +; Compute the random record number. Enter with (HL) pointing +; to a fcb an (DE) contains a relative location of a record +; number. On exit, (C) contains the 'r0' byte, (B) the 'r1' +; byte, and (A) the 'r2' byte. +; +; On return, the zero flag is set if the record is within +; bounds. Otherwise, an overflow occured. +; +COMPRAND: EX DE,HL ;save fcb pointer in (DE). + ADD HL,DE ;compute relative position of record #. + LD C,(HL) ;get record number into (BC). + LD B,0 + LD HL,12 ;now get extent. + ADD HL,DE + LD A,(HL) ;compute (BC)=(record #)+(extent)*128. + RRCA ;move lower bit into bit 7. + AND 80H ;and ignore all other bits. + ADD A,C ;add to our record number. + LD C,A + LD A,0 ;take care of any carry. + ADC A,B + LD B,A + LD A,(HL) ;now get the upper bits of extent into + RRCA ;bit positions 0-3. + AND 0FH ;and ignore all others. + ADD A,B ;add this in to 'r1' byte. + LD B,A + LD HL,14 ;get the 's2' byte (extra extent). + ADD HL,DE + LD A,(HL) + ADD A,A ;and shift it left 4 bits (bits 4-7). + ADD A,A + ADD A,A + ADD A,A + PUSH AF ;save carry flag (bit 0 of flag byte). + ADD A,B ;now add extra extent into 'r1'. + LD B,A + PUSH AF ;and save carry (overflow byte 'r2'). + POP HL ;bit 0 of (L) is the overflow indicator. + LD A,L + POP HL ;and same for first carry flag. + OR L ;either one of these set? + AND 01H ;only check the carry flags. + RET +; +; Routine to setup the fcb (bytes 'r0', 'r1', 'r2') to +; reflect the last record used for a random (or other) file. +; This reads the directory and looks at all extents computing +; the largerst record number for each and keeping the maximum +; value only. Then 'r0', 'r1', and 'r2' will reflect this +; maximum record number. This is used to compute the space used +; by a random file. +; +RANSIZE:LD C,12 ;look thru directory for first entry with + CALL FINDFST ;this name. + LD HL,(PARAMS) ;zero out the 'r0, r1, r2' bytes. + LD DE,33 + ADD HL,DE + PUSH HL + LD (HL),D ;note that (D)=0. + INC HL + LD (HL),D + INC HL + LD (HL),D +RANSIZ1:CALL CKFILPOS ;is there an extent to process? + JP Z,RANSIZ3 ;no, we are done. + CALL FCB2HL ;set (HL) pointing to proper fcb in dir. + LD DE,15 ;point to last record in extent. + CALL COMPRAND ;and compute random parameters. + POP HL + PUSH HL ;now check these values against those + LD E,A ;already in fcb. + LD A,C ;the carry flag will be set if those + SUB (HL) ;in the fcb represent a larger size than + INC HL ;this extent does. + LD A,B + SBC A,(HL) + INC HL + LD A,E + SBC A,(HL) + JP C,RANSIZ2 + LD (HL),E ;we found a larger (in size) extent. + DEC HL ;stuff these values into fcb. + LD (HL),B + DEC HL + LD (HL),C +RANSIZ2:CALL FINDNXT ;now get the next extent. + JP RANSIZ1 ;continue til all done. +RANSIZ3:POP HL ;we are done, restore the stack and + RET ;return. +; +; Function to return the random record position of a given +; file which has been read in sequential mode up to now. +; +SETRAN: LD HL,(PARAMS) ;point to fcb. + LD DE,32 ;and to last used record. + CALL COMPRAND ;compute random position. + LD HL,33 ;now stuff these values into fcb. + ADD HL,DE + LD (HL),C ;move 'r0'. + INC HL + LD (HL),B ;and 'r1'. + INC HL + LD (HL),A ;and lastly 'r2'. + RET +; +; This routine select the drive specified in (ACTIVE) and +; update the login vector and bitmap table if this drive was +; not already active. +; +LOGINDRV: LD HL,(LOGIN) ;get the login vector. + LD A,(ACTIVE) ;get the default drive. + LD C,A + CALL SHIFTR ;position active bit for this drive + PUSH HL ;into bit 0. + EX DE,HL + CALL SELECT ;select this drive. + POP HL + CALL Z,SLCTERR ;valid drive? + LD A,L ;is this a newly activated drive? + RRA + RET C + LD HL,(LOGIN) ;yes, update the login vector. + LD C,L + LD B,H + CALL SETBIT + LD (LOGIN),HL ;and save. + JP BITMAP ;now update the bitmap. +; +; Function to set the active disk number. +; +SETDSK: LD A,(EPARAM) ;get parameter passed and see if this + LD HL,ACTIVE ;represents a change in drives. + CP (HL) + RET Z + LD (HL),A ;yes it does, log it in. + JP LOGINDRV +; +; This is the 'auto disk select' routine. The firsst byte +; of the fcb is examined for a drive specification. If non +; zero then the drive will be selected and loged in. +; +AUTOSEL:LD A,0FFH ;say 'auto-select activated'. + LD (AUTO),A + LD HL,(PARAMS) ;get drive specified. + LD A,(HL) + AND 1FH ;look at lower 5 bits. + DEC A ;adjust for (1=A, 2=B) etc. + LD (EPARAM),A ;and save for the select routine. + CP 1EH ;check for 'no change' condition. + JP NC,AUTOSL1 ;yes, don't change. + LD A,(ACTIVE) ;we must change, save currently active + LD (OLDDRV),A ;drive. + LD A,(HL) ;and save first byte of fcb also. + LD (AUTOFLAG),A ;this must be non-zero. + AND 0E0H ;whats this for (bits 6,7 are used for + LD (HL),A ;something)? + CALL SETDSK ;select and log in this drive. +AUTOSL1:LD A,(USERNO) ;move user number into fcb. + LD HL,(PARAMS) ;(* upper half of first byte *) + OR (HL) + LD (HL),A + RET ;and return (all done). +; +; Function to return the current cp/m version number. +; +GETVER: LD A,022H ;version 2.2 + JP SETSTAT +; +; Function to reset the disk system. +; +RSTDSK: LD HL,0 ;clear write protect status and log + LD (WRTPRT),HL ;in vector. + LD (LOGIN),HL + XOR A ;select drive 'A'. + LD (ACTIVE),A + LD HL,TBUFF ;setup default dma address. + LD (USERDMA),HL + CALL DEFDMA + JP LOGINDRV ;now log in drive 'A'. +; +; Function to open a specified file. +; +OPENFIL:CALL CLEARS2 ;clear 's2' byte. + CALL AUTOSEL ;select proper disk. + JP OPENIT ;and open the file. +; +; Function to close a specified file. +; +CLOSEFIL: CALL AUTOSEL ;select proper disk. + JP CLOSEIT ;and close the file. +; +; Function to return the first occurence of a specified file +; name. If the first byte of the fcb is '?' then the name will +; not be checked (get the first entry no matter what). +; +GETFST: LD C,0 ;prepare for special search. + EX DE,HL + LD A,(HL) ;is first byte a '?'? + CP '?' + JP Z,GETFST1 ;yes, just get very first entry (zero length match). + CALL SETEXT ;get the extension byte from fcb. + LD A,(HL) ;is it '?'? if yes, then we want + CP '?' ;an entry with a specific 's2' byte. + CALL NZ,CLEARS2 ;otherwise, look for a zero 's2' byte. + CALL AUTOSEL ;select proper drive. + LD C,15 ;compare bytes 0-14 in fcb (12&13 excluded). +GETFST1:CALL FINDFST ;find an entry and then move it into + JP MOVEDIR ;the users dma space. +; +; Function to return the next occurence of a file name. +; +GETNXT: LD HL,(SAVEFCB) ;restore pointers. note that no + LD (PARAMS),HL ;other dbos calls are allowed. + CALL AUTOSEL ;no error will be returned, but the + CALL FINDNXT ;results will be wrong. + JP MOVEDIR +; +; Function to delete a file by name. +; +DELFILE:CALL AUTOSEL ;select proper drive. + CALL ERAFILE ;erase the file. + JP STSTATUS ;set status and return. +; +; Function to execute a sequential read of the specified +; record number. +; +READSEQ:CALL AUTOSEL ;select proper drive then read. + JP RDSEQ +; +; Function to write the net sequential record. +; +WRTSEQ: CALL AUTOSEL ;select proper drive then write. + JP WTSEQ +; +; Create a file function. +; +FCREATE:CALL CLEARS2 ;clear the 's2' byte on all creates. + CALL AUTOSEL ;select proper drive and get the next + JP GETEMPTY ;empty directory space. +; +; Function to rename a file. +; +RENFILE:CALL AUTOSEL ;select proper drive and then switch + CALL CHGNAMES ;file names. + JP STSTATUS +; +; Function to return the login vector. +; +GETLOG: LD HL,(LOGIN) + JP GETPRM1 +; +; Function to return the current disk assignment. +; +GETCRNT:LD A,(ACTIVE) + JP SETSTAT +; +; Function to set the dma address. +; +PUTDMA: EX DE,HL + LD (USERDMA),HL ;save in our space and then get to + JP DEFDMA ;the bios with this also. +; +; Function to return the allocation vector. +; +GETALOC:LD HL,(ALOCVECT) + JP GETPRM1 +; +; Function to return the read-only status vector. +; +GETROV: LD HL,(WRTPRT) + JP GETPRM1 +; +; Function to set the file attributes (read-only, system). +; +SETATTR:CALL AUTOSEL ;select proper drive then save attributes. + CALL SAVEATTR + JP STSTATUS +; +; Function to return the address of the disk parameter block +; for the current drive. +; +GETPARM:LD HL,(DISKPB) +GETPRM1:LD (STATUS),HL + RET +; +; Function to get or set the user number. If (E) was (FF) +; then this is a request to return the current user number. +; Else set the user number from (E). +; +GETUSER:LD A,(EPARAM) ;get parameter. + CP 0FFH ;get user number? + JP NZ,SETUSER + LD A,(USERNO) ;yes, just do it. + JP SETSTAT +SETUSER:AND 1FH ;no, we should set it instead. keep low + LD (USERNO),A ;bits (0-4) only. + RET +; +; Function to read a random record from a file. +; +RDRANDOM: CALL AUTOSEL ;select proper drive and read. + JP READRAN +; +; Function to compute the file size for random files. +; +WTRANDOM: CALL AUTOSEL ;select proper drive and write. + JP WRITERAN +; +; Function to compute the size of a random file. +; +FILESIZE: CALL AUTOSEL ;select proper drive and check file length + JP RANSIZE +; +; Function #37. This allows a program to log off any drives. +; On entry, set (DE) to contain a word with bits set for those +; drives that are to be logged off. The log-in vector and the +; write protect vector will be updated. This must be a M/PM +; special function. +; +LOGOFF: LD HL,(PARAMS) ;get drives to log off. + LD A,L ;for each bit that is set, we want + CPL ;to clear that bit in (LOGIN) + LD E,A ;and (WRTPRT). + LD A,H + CPL + LD HL,(LOGIN) ;reset the login vector. + AND H + LD D,A + LD A,L + AND E + LD E,A + LD HL,(WRTPRT) + EX DE,HL + LD (LOGIN),HL ;and save. + LD A,L ;now do the write protect vector. + AND E + LD L,A + LD A,H + AND D + LD H,A + LD (WRTPRT),HL ;and save. all done. + RET +; +; Get here to return to the user. +; +GOBACK: LD A,(AUTO) ;was auto select activated? + OR A + JP Z,GOBACK1 + LD HL,(PARAMS) ;yes, but was a change made? + LD (HL),0 ;(* reset first byte of fcb *) + LD A,(AUTOFLAG) + OR A + JP Z,GOBACK1 + LD (HL),A ;yes, reset first byte properly. + LD A,(OLDDRV) ;and get the old drive and select it. + LD (EPARAM),A + CALL SETDSK +GOBACK1:LD HL,(USRSTACK) ;reset the users stack pointer. + LD SP,HL + LD HL,(STATUS) ;get return status. + LD A,L ;force version 1.4 compatability. + LD B,H + RET ;and go back to user. +; +; Function #40. This is a special entry to do random i/o. +; For the case where we are writing to unused disk space, this +; space will be zeroed out first. This must be a M/PM special +; purpose function, because why would any normal program even +; care about the previous contents of a sector about to be +; written over. +; +WTSPECL:CALL AUTOSEL ;select proper drive. + LD A,2 ;use special write mode. + LD (MODE),A + LD C,0 ;set write indicator. + CALL POSITN1 ;position the file. + CALL Z,WTSEQ1 ;and write (if no errors). + RET +; +;************************************************************** +;* +;* BDOS data storage pool. +;* +;************************************************************** +; +EMPTYFCB: .DB 0E5H ;empty directory segment indicator. +WRTPRT: .DW 0 ;write protect status for all 16 drives. +LOGIN: .DW 0 ;drive active word (1 bit per drive). +USERDMA:.DW 080H ;user's dma address (defaults to 80h). +; +; Scratch areas from parameter block. +; +SCRATCH1: .DW 0 ;relative position within dir segment for file (0-3). +SCRATCH2: .DW 0 ;last selected track number. +SCRATCH3: .DW 0 ;last selected sector number. +; +; Disk storage areas from parameter block. +; +DIRBUF: .DW 0 ;address of directory buffer to use. +DISKPB: .DW 0 ;contains address of disk parameter block. +CHKVECT:.DW 0 ;address of check vector. +ALOCVECT: .DW 0 ;address of allocation vector (bit map). +; +; Parameter block returned from the bios. +; +SECTORS:.DW 0 ;sectors per track from bios. +BLKSHFT:.DB 0 ;block shift. +BLKMASK:.DB 0 ;block mask. +EXTMASK:.DB 0 ;extent mask. +DSKSIZE:.DW 0 ;disk size from bios (number of blocks-1). +DIRSIZE:.DW 0 ;directory size. +ALLOC0: .DW 0 ;storage for first bytes of bit map (dir space used). +ALLOC1: .DW 0 +OFFSET: .DW 0 ;first usable track number. +XLATE: .DW 0 ;sector translation table address. +; +; +CLOSEFLG: .DB 0 ;close flag (=0ffh is extent written ok). +RDWRTFLG: .DB 0 ;read/write flag (0ffh=read, 0=write). +FNDSTAT:.DB 0 ;filename found status (0=found first entry). +MODE: .DB 0 ;I/o mode select (0=random, 1=sequential, 2=special random). +EPARAM: .DB 0 ;storage for register (E) on entry to bdos. +RELBLOCK: .DB 0 ;relative position within fcb of block number written. +COUNTER:.DB 0 ;byte counter for directory name searches. +SAVEFCB:.DW 0,0 ;save space for address of fcb (for directory searches). +BIGDISK:.DB 0 ;if =0 then disk is > 256 blocks long. +AUTO: .DB 0 ;if non-zero, then auto select activated. +OLDDRV: .DB 0 ;on auto select, storage for previous drive. +AUTOFLAG: .DB 0 ;if non-zero, then auto select changed drives. +SAVNXT: .DB 0 ;storage for next record number to access. +SAVEXT: .DB 0 ;storage for extent number of file. +SAVNREC:.DW 0 ;storage for number of records in file. +BLKNMBR:.DW 0 ;block number (physical sector) used within a file or logical sect +LOGSECT:.DW 0 ;starting logical (128 byte) sector of block (physical sector). +FCBPOS: .DB 0 ;relative position within buffer for fcb of file of interest. +FILEPOS:.DW 0 ;files position within directory (0 to max entries -1). +; +; Disk directory buffer checksum bytes. One for each of the +; 16 possible drives. +; +CKSUMTBL: .DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +; +; Extra space ? +; + .DB 0,0,0,0 +; +;************************************************************** +;* +;* B I O S J U M P T A B L E +;* +;************************************************************** +; +BOOT: JP 0 ;NOTE WE USE FAKE DESTINATIONS +WBOOT: JP 0 +CONST: JP 0 +CONIN: JP 0 +CONOUT: JP 0 +LIST: JP 0 +PUNCH: JP 0 +READER: JP 0 +HOME: JP 0 +SELDSK: JP 0 +SETTRK: JP 0 +SETSEC: JP 0 +SETDMA: JP 0 +READ: JP 0 +WRITE: JP 0 +PRSTAT: JP 0 +SECTRN: JP 0 +; +;* +;****************** E N D O F C P / M ***************** +;* + + .END diff --git a/Z80 CPM and bootloader (basmon)/source/download.asm b/Z80 CPM and bootloader (basmon)/source/download.asm new file mode 100644 index 0000000..d253e9f --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/download.asm @@ -0,0 +1,287 @@ +;================================================================================== +; Contents of this file are copyright Grant Searle +; HEX routine from Joel Owens. +; +; You have permission to use this for NON COMMERCIAL USE ONLY +; If you wish to use it elsewhere, please include an acknowledgement to myself. +; +; http://searle.hostei.com/grant/index.html +; +; eMail: home.micros01@btinternet.com +; +; If the above don't work, please perform an Internet search to see if I have +; updated the web page hosting service. +; +;================================================================================== + +TPA .EQU 100H +REBOOT .EQU 0H +BDOS .EQU 5H +CONIO .EQU 6 +CONINP .EQU 1 +CONOUT .EQU 2 +PSTRING .EQU 9 +MAKEF .EQU 22 +CLOSEF .EQU 16 +WRITES .EQU 21 +DELF .EQU 19 +SETUSR .EQU 32 + +CR .EQU 0DH +LF .EQU 0AH + +FCB .EQU 05CH +BUFF .EQU 080H + + .ORG TPA + + + LD A,0 + LD (buffPos),A + LD (checkSum),A + LD (byteCount),A + LD (printCount),A + LD HL,BUFF + LD (buffPtr),HL + + +WAITLT: CALL GETCHR + CP 'U' + JP Z,SETUSER + CP ':' + JR NZ,WAITLT + + + LD C,DELF + LD DE,FCB + CALL BDOS + + LD C,MAKEF + LD DE,FCB + CALL BDOS + +GETHEX: + CALL GETCHR + CP '>' + JR Z,CLOSE + LD B,A + PUSH BC + CALL GETCHR + POP BC + LD C,A + + CALL BCTOA + + LD B,A + LD A,(checkSum) + ADD A,B + LD (checkSum),A + LD A,(byteCount) + INC A + LD (byteCount),A + + LD A,B + + LD HL,(buffPtr) + + LD (HL),A + INC HL + LD (buffPtr),HL + + LD A,(buffPos) + INC A + LD (buffPos),A + CP 80H + + JR NZ,NOWRITE + + LD C,WRITES + LD DE,FCB + CALL BDOS + LD A,'.' + CALL PUTCHR + + ; New line every 8K (64 dots) + LD A,(printCount) + INC A + CP 64 + JR NZ,noCRLF + LD (printCount),A + LD A,CR + CALL PUTCHR + LD A,LF + CALL PUTCHR + LD A,0 +noCRLF: LD (printCount),A + + LD HL,BUFF + LD (buffPtr),HL + + LD A,0 + LD (buffPos),A +NOWRITE: + JR GETHEX + + +CLOSE: + + LD A,(buffPos) + CP 0 + JR Z,NOWRITE2 + + LD C,WRITES + LD DE,FCB + CALL BDOS + LD A,'.' + CALL PUTCHR + +NOWRITE2: + LD C,CLOSEF + LD DE,FCB + CALL BDOS + +; Byte count (lower 8 bits) + CALL GETCHR + LD B,A + PUSH BC + CALL GETCHR + POP BC + LD C,A + + CALL BCTOA + LD B,A + LD A,(byteCount) + SUB B + CP 0 + JR Z,byteCountOK + + LD A,CR + CALL PUTCHR + LD A,LF + CALL PUTCHR + + LD DE,countErrMess + LD C,PSTRING + CALL BDOS + + ; Sink remaining 2 bytes + CALL GETCHR + CALL GETCHR + + JR FINISH + +byteCountOK: + +; Checksum + CALL GETCHR + LD B,A + PUSH BC + CALL GETCHR + POP BC + LD C,A + + CALL BCTOA + LD B,A + LD A,(checkSum) + SUB B + CP 0 + JR Z,checksumOK + + LD A,CR + CALL PUTCHR + LD A,LF + CALL PUTCHR + + LD DE,chkErrMess + LD C,PSTRING + CALL BDOS + JR FINISH + +checksumOK: + LD A,CR + CALL PUTCHR + LD A,LF + CALL PUTCHR + + LD DE,OKMess + LD C,PSTRING + CALL BDOS + + + +FINISH: + LD C,SETUSR + LD E,0 + CALL BDOS + + JP REBOOT + + +SETUSER: + CALL GETCHR + CALL HEX2VAL + LD E,A + LD C,SETUSR + CALL BDOS + JP WAITLT + + +; Get a char into A +;GETCHR: LD C,CONINP +; CALL BDOS +; RET + +; Wait for a char into A (no echo) +GETCHR: + LD E,$FF + LD C,CONIO + CALL BDOS + CP 0 + JR Z,GETCHR + RET + +; Write A to output +PUTCHR: LD C,CONOUT + LD E,A + CALL BDOS + RET + + +;------------------------------------------------------------------------------ +; Convert ASCII characters in B C registers to a byte value in A +;------------------------------------------------------------------------------ +BCTOA LD A,B ; Move the hi order byte to A + SUB $30 ; Take it down from Ascii + CP $0A ; Are we in the 0-9 range here? + JR C,BCTOA1 ; If so, get the next nybble + SUB $07 ; But if A-F, take it down some more +BCTOA1 RLCA ; Rotate the nybble from low to high + RLCA ; One bit at a time + RLCA ; Until we + RLCA ; Get there with it + LD B,A ; Save the converted high nybble + LD A,C ; Now get the low order byte + SUB $30 ; Convert it down from Ascii + CP $0A ; 0-9 at this point? + JR C,BCTOA2 ; Good enough then, but + SUB $07 ; Take off 7 more if it's A-F +BCTOA2 ADD A,B ; Add in the high order nybble + RET + +; Change Hex in A to actual value in A +HEX2VAL SUB $30 + CP $0A + RET C + SUB $07 + RET + + +buffPos .DB 0H +buffPtr .DW 0000H +printCount .DB 0H +checkSum .DB 0H +byteCount .DB 0H +OKMess .BYTE "OK$" +chkErrMess .BYTE "======Checksum Error======$" +countErrMess .BYTE "======File Length Error======$" + .END diff --git a/Z80 CPM and bootloader (basmon)/source/form128.asm b/Z80 CPM and bootloader (basmon)/source/form128.asm new file mode 100644 index 0000000..70c8f65 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/form128.asm @@ -0,0 +1,220 @@ +;================================================================================== +; Contents of this file are copyright Grant Searle +; +; You have permission to use this for NON COMMERCIAL USE ONLY +; If you wish to use it elsewhere, please include an acknowledgement to myself. +; +; http://searle.hostei.com/grant/index.html +; +; eMail: home.micros01@btinternet.com +; +; If the above don't work, please perform an Internet search to see if I have +; updated the web page hosting service. +; +;================================================================================== + +numDrives .EQU 15 ; Not including A: + + +SD_DATA .EQU 088H +SD_CONTROL .EQU 089H +SD_STATUS .EQU 089H +SD_LBA0 .EQU 08AH +SD_LBA1 .EQU 08BH +SD_LBA2 .EQU 08CH + +LF .EQU 0AH ;line feed +FF .EQU 0CH ;form feed +CR .EQU 0DH ;carriage RETurn + +;==================================================================================== + + .ORG 5000H ; Format program origin. + + + CALL printInline + .TEXT "CP/M Formatter 2.0 by G. Searle 2013" + .DB CR,LF,0 + + LD A,'A' + LD (drvName),A + +; There are 512 directory entries per disk, 4 DIR entries per sector +; So 128 x 128 byte sectors are to be initialised +; The drive uses 512 byte sectors, so 32 x 512 byte sectors per disk +; require initialisation + +;Drive 0 (A:) is slightly different due to reserved track, so DIR sector starts at 32 + LD A,(drvName) + RST 08H ; Print drive letter + INC A + LD (drvName),A + + LD A,$20 + LD (secNo),A + +processSectorA: + + LD A,(secNo) + OUT (SD_LBA0),A + LD A,0 + OUT (SD_LBA1),A + LD A,0 + OUT (SD_LBA2),A + LD a,$E0 + + call writehst + + LD A,(secNo) + INC A + LD (secNo),A + CP $40 + JR NZ, processSectorA + + + +;Drive 1 onwards (B: etc) don't have reserved tracks, so sector starts at 0 + + LD DE,$0040 ; HL increment + LD HL,$0040 ; H = LBA2, L=LBA1, initialise for drive 1 (B:) + + LD B,numDrives + +processDirs: + + LD A,(drvName) + RST 08H ; Print drive letter + INC A + LD (drvName),A + + LD A,0 + LD (secNo),A + +processSector: + LD A,(secNo) + OUT (SD_LBA0),A + LD A,L + OUT (SD_LBA1),A + LD A,H + OUT (SD_LBA2),A + + call writehst + + LD A,(secNo) + INC A + LD (secNo),A + CP $20 + JR NZ, processSector + + ADD HL,DE + + DEC B + JR NZ,processDirs + + CALL printInline + .DB CR,LF + .TEXT "Formatting complete" + .DB CR,LF,0 + + RET + +;================================================================================================ +; Write physical sector to host +;================================================================================================ + +writehst: + PUSH AF + PUSH BC + PUSH HL + +wrWait1: IN A,(SD_STATUS) + CP 128 + JR NZ,wrWait1 + + ;CALL setLBAaddr + + LD A,$01 ; 01 = Write block + OUT (SD_CONTROL),A + + LD c,4 +wr4secs: + LD HL,dirData + LD b,128 +wrByte: +wrWait2: IN A,(SD_STATUS) + CP 160 ; Write buffer empty + JR NZ,wrWait2 + + ;LD A,'.' + ;RST 08H + + ; UPDATE S0urceror, inserted wait cycle between IN and OUT + ; to resolve unknown write issue in sd_controller.vhd in combination + ; with MISTer virtual SD interface sys/sd_card.sv + ; which results in hangs or write errors. + push bc + ld b,100 +_again: + djnz _again + pop bc + ; END UPDATE + + LD A,(HL) + OUT (SD_DATA),A + + INC HL + dec b + JR NZ, wrByte + + dec c + JR NZ,wr4secs + + POP HL + POP BC + POP AF + + ;XOR a + ;ld (erflag),a + RET + +;================================================================================================ +; Utilities +;================================================================================================ + +printInline: + EX (SP),HL ; PUSH HL and put RET ADDress into HL + PUSH AF + PUSH BC +nextILChar: LD A,(HL) + CP 0 + JR Z,endOfPrint + RST 08H + INC HL + JR nextILChar +endOfPrint: INC HL ; Get past "null" terminator + POP BC + POP AF + EX (SP),HL ; PUSH new RET ADDress on stack and restore HL + RET + + +secNo .db 0 +drvName .db 0 + + +; Directory data for 1 x 128 byte sector +dirData: + .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 + .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + + .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 + .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + + .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 + .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + + .DB $E5,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$00,$00,$00,$00 + .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + + .END + \ No newline at end of file diff --git a/Z80 CPM and bootloader (basmon)/source/putsys.asm b/Z80 CPM and bootloader (basmon)/source/putsys.asm new file mode 100644 index 0000000..9bde989 --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/source/putsys.asm @@ -0,0 +1,163 @@ +;================================================================================== +; Contents of this file are copyright Grant Searle +; +; You have permission to use this for NON COMMERCIAL USE ONLY +; If you wish to use it elsewhere, please include an acknowledgement to myself. +; +; http://searle.hostei.com/grant/index.html +; +; eMail: home.micros01@btinternet.com +; +; If the above don't work, please perform an Internet search to see if I have +; updated the web page hosting service. +; +;================================================================================== + +loadAddr .EQU 0D000h +numSecs .EQU 24 ; Number of 512 sectors to be loaded + +SD_DATA .EQU 088H +SD_CONTROL .EQU 089H +SD_STATUS .EQU 089H +SD_LBA0 .EQU 08AH +SD_LBA1 .EQU 08BH +SD_LBA2 .EQU 08CH + +LF .EQU 0AH ;line feed +FF .EQU 0CH ;form feed +CR .EQU 0DH ;carriage RETurn + +;================================================================================================ + + .ORG 5000H ; Loader origin. + + CALL printInline + .TEXT "CP/M System Transfer by G. Searle 2012-13" + .DB CR,LF,0 + + LD B,numSecs + + LD A,0 + LD (lba0),A + ld (lba1),A + ld (lba2),A + ld (lba3),A + LD HL,loadAddr + LD (dmaAddr),HL +processSectors: + + call writehst + + LD DE,0200H + LD HL,(dmaAddr) + ADD HL,DE + LD (dmaAddr),HL + LD A,(lba0) + INC A + LD (lba0),A + + djnz processSectors + + CALL printInline + .DB CR,LF + .TEXT "System transfer complete" + .DB CR,LF,0 + + RET + +; ========================================================================= +; Disk routines as used in CBIOS +; ========================================================================= +setLBAaddr: + LD A,(lba2) + OUT (SD_LBA2),A + LD A,(lba1) + OUT (SD_LBA1),A + LD A,(lba0) + OUT (SD_LBA0),A + ret + +;================================================================================================ +; Write physical sector to host +;================================================================================================ + +writehst: + PUSH AF + PUSH BC + PUSH HL + +wrWait1: IN A,(SD_STATUS) + CP 128 + JR NZ,wrWait1 + + CALL setLBAaddr + + LD A,$01 ; 01 = Write block + OUT (SD_CONTROL),A + + LD c,4 + ;LD HL,hstbuf +wr4secs: + LD b,128 +wrByte: + +wrWait2: IN A,(SD_STATUS) + CP 160 ; Write buffer empty + JR NZ,wrWait2 + + ; UPDATE S0urceror, inserted wait cycle between IN and OUT + ; to resolve unknown write issue in sd_controller.vhd in combination + ; with MISTer virtual SD interface sys/sd_card.sv + ; which results in hangs or write errors. + push bc + ld b,100 +_again: + djnz _again + pop bc + ; END UPDATE + + LD A,(HL) + OUT (SD_DATA),A + INC HL + dec b + JR NZ, wrByte + + dec c + JR NZ,wr4secs + + POP HL + POP BC + POP AF + + ;XOR a + ;ld (erflag),a + RET + + +;================================================================================================ +; Utilities +;================================================================================================ + +printInline: + EX (SP),HL ; PUSH HL and put RET ADDress into HL + PUSH AF + PUSH BC +nextILChar: LD A,(HL) + CP 0 + JR Z,endOfPrint + RST 08H + INC HL + JR nextILChar +endOfPrint: INC HL ; Get past "null" terminator + POP BC + POP AF + EX (SP),HL ; PUSH new RET ADDress on stack and restore HL + RET + +lba0 .DB 00h +lba1 .DB 00h +lba2 .DB 00h +lba3 .DB 00h +dmaAddr .dw 0 + + .END diff --git a/Z80 CPM and bootloader (basmon)/transientAppsPackage/CPM211FilesPkg.txt b/Z80 CPM and bootloader (basmon)/transientAppsPackage/CPM211FilesPkg.txt new file mode 100644 index 0000000..1a035ae --- /dev/null +++ b/Z80 CPM and bootloader (basmon)/transientAppsPackage/CPM211FilesPkg.txt @@ -0,0 +1,27 @@ +A:DOWNLOAD LOAD.COM +U0 +:C3400220434F505952494748542028432920313937382C204449474954414C205245534541524348204552524F523A20242C204C4F4144204144445245535320244449534B205245414424494E564552544544204C4F41442041444452455353244449534B205752495445244C4F414420204144445245535320244552524F5220414444524553532024425954455320524541443A24494E56414C49442048455820444947495424434845434B2053554D204552524F5220244649525354204144445245535320244C41535420204144445245535320244259544553205245414420202020245245434F524453205752495454454E20244845580043414E4E4F54204F50454E20534F5552434524434F4D4E4F204D4F5245204449524543544F52592053504143452443414E4E4F5420434C4F53452046494C45242AE707F9C92100003922E70721650DF921000122160C210004220B0C015C00C51E2101E907CD220401F701C51E0401F207CD220401E907CD8A033A1A0CFEFFC2830201FB01CD6403010E02C51E03016500CD2204015C00CDCF03015C00CDFF03015C00CD8A033A1A0CFEFFC2B202011102CD6403C3C902CDE404015C00CD9D033A1A0CFEFFC2C902012902CD6403CDE002CD3B02C9210D0C712A0D0C2600EB0E02CD6607C90E0DCDD0020E0ACDD002C9210E0C713E09210E0CBED206033A0E0CC641D60A4FCDD002C30F033A0E0CC6304FCDD002C9210F0C713A0F0CE6F81F1F1F1F4FCDEB023A0F0CE60F4FCDEB02C921110C702B712A100C7C4FCD10032A100C7D4FCD1003C921130C702B712A120CEB0E09CD6607C921150C702B71CDE0022A140C444DCD4203C921190C702B71012901CD52032A180C444DCD4203013101CD42032A160C444DCD2B03CD3B02C9211C0C702B712A1B0CEB0E0FCD6907321A0CC9211E0C702B712A1D0CEB0E10CD6907321A0CC921200C702B712A1F0CEB0E11CD6907321A0CC91100000E12CD6907321A0CC921220C702B712A210CEB0E13CD6607C921240C702B712A230CEB0E14CD6907C921260C702B712A250CEB0E15CD6907C921280C702B712A270CEB0E16CD6907321A0CC9212A0C702B712A290CEB0E17CD6607C9212F0C732B702B712BD1C1702B71D53A2F0C3D322F0CFEFFCA58042A2B0CE52A2D0CC10A772A2B0C23222B0C2A2D0C23222D0CC33104C92A0B0C23220B0C11FF03CD9607DA72042A0B0C010A08097EC9210000220B0C11FF03210B0CCDB807DADA04C397041180002A0B0C19220B0CD27804C3DA0401E907CDDF0332300CFE00C2BA04018000C52A0B0C010A0809444D1E80CD2204C3D7043A300CFE01CAC804014101CD64032A0B0C010A0809361A21FF03220B0CC38704210000220B0C3A0A08C921000022360C22380C223A0C7D323C0D21000122340C223D0D210A08361ACD5904FE3ACA0D05C3020521320C3600CD3D0732310CFE00C22005C3A1053A310C113A0CCD7207EB2B732372CD3D07F5CD3D075FC148CD4C0722340C22160C3E0011360CCDAA07B5C253052A160C22360CCD3D0732330C3A310C3D32310CFEFFCA7605CD3D074FCDFD052A160C2322160CC3590511380C01160CCD9D07D289052A160C2B22380CCD2E0721320C86FE00CA9E0501A801CD5203CD8006C302052A160C22340C01340C113D0DCD9D07D2C2050E00CDFD052A160C2322160CC3A70501B901CD52032A360C444DCD2B0301C801CD52032A380C444DCD2B0301D701CD52032A3A0C444DCD2B0301E601CD52032A3C0D4DCD1003CDE002C9213F0D71013D0D11160CCD9D07D21306014B01CD640311FF002A3D0D19EB21160CCDB807D2700621400D36003E7F21400DBEDA58062A3D0D7D4F0600213C0C09E52A400D260001800009D11A772A3D0D23223D0D21400D34C22906213C0D34015C00CDEF03FE00CA6D06016101CD6403C313062A160C7D4F0600213C0C093A3F0D77C9016C01CD52032A340C444DCD2B03017B01CD52032A160C444DCD2B03018A01CD5203CDE60601160C11340CCD9D07D2DF062A340C7DE60FFE00C2BF06CDE606013D0D11340CCD9D07013C0C094ECD10032A340C2322340C0E20CDD002C3A506CDE002CD3B02C9CDE0022A340C444DCD2B030E3ACDD0020E20CDD002C9CD590432410DD6304F3E09B9DA11073A410DD630C93A410DD6414F3E05B9D22607019601CD5203CD80063A410DD641C60AC9CDFC0687878787F5CDFC06C148B1C9CD2E0732420D21320C86773A420DC921440D732B712A430D26000E08CD8D073A440DCD7F07C9C30000C30500C30500CD0500C9C9C9EB5F1600EB1A856F131A8C67C95F16007BB56F7AB467C95E2356EB290DC28D07C95F16007B956F7A9C67C969604E23461A916F131A9867C96F26001A956F131A9C67C95F16007B965F7A239E57EBC91A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A>0047 +A:DOWNLOAD PIP.COM +U0 +:C3CE04C90000C900001A00000000000028494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A53504143452928494E503A2F4F55543A535041434529202020434F505952494748542028432920313937392C204449474954414C2052455345415243482C2020504950205645525320312E3503010601002424242020202020535542000000203D2E3A2C3C3E0D5F5B5D494E504952445054525552315552325244524F55544C5054554C3150524E4C535450545055503155503250554E545459435254554331434F4E4E554C454F46004449534B2052454144204552524F52244449534B205752495445204552524F5224564552494659204552524F52244E4F542041204348415241435445522053494E4B245245414445522053544F5050494E470D0A244E4F5420412043484152414354455220534F555243452441424F525445442442414420504152414D4554455224494E56414C49442055534552204E554D424552245245434F524420544F4F204C4F4E4724494E56414C494420444947495424454E44204F462046494C452C2043544C2D5A3F24434845434B53554D204552524F5224434F5252454354204552524F522C20545950452052455455524E204F522043544C2D5A24494E56414C494420464F524D415424484558242424244E4F204449524543544F5259205350414345244E4F2046494C4524434F4D245354415254204E4F5420464F554E442451554954204E4F5420464F554E442443414E4E4F5420434C4F53452044455354494E4154494F4E2046494C452444455354494E4154494F4E20495320522F4F2C2044454C4554452028592F4E293F242A2A4E4F542044454C455445442A2A242424242424244E4F5420464F554E4424434F5059494E47202D2452455155495245532043502F4D20322E30204F52204E4557455220464F52204F5045524154494F4E2E24554E5245434F474E495A45442044455354494E4154494F4E2443414E4E4F5420575249544524494E56414C49442050495020464F524D41542443414E4E4F54205245414424494E56414C494420534550415241544F522431F21D018000C51E8001CC1ECD180A3ACC1ED600D6019F32A51ECD4C08EB3E20CD841DD2FD04014D04CD3908CD0000CD160932C01E1100000E19CD050032FC1D31F21DCD401A3AC01E32C11E216F1F36002B36002B360021A61E360123360021F31D36002336FE3AA51E1FD247050E2ACD1C08CD6F09CD2E08214E1F36FF3ACC1EFE00C25E052AFC1D4DCD5E08CD0000214B1E360021031E360021A41E36002B360001271ECD20123AA91EFE03C28105C324063AA91EFE04C2C3053A941F3D324B1ECD0C1D01061ECD20123AA91EFE02CAA405CD5C183AF51D1FD2B705015C00CDEE1CCD781BC3C00501271ECDEE1CCDB21AC3DB073AA91ED602C6FF9F21F51DB61FD2D605CD5C18CDA21CCD0C1D01061ECD20123AA91EFE04C20506CD881CCDC61C01271EC51E2101061ECD180ACDDF1CCDB21AC3DB073AA91EFE02C22406CD11123AA81EFE0DCA1B06C32406CD881CCDB21AC3DB07214E1F36FF01271ECD20123AA91ED6029F21F51DB61FD24306017704CDAF0921051E36003AA91EFE02C25E06CDA21CCD631821A81E36FFC37B063AA81ED6139F2FF53E0521A81E969F2FC148B11FD27B06019004CDAF093AA81E3C32A31EFE0FC28A06CDCF1501061ECD20123AA91ED601C6FF9FF53AA81ED63DC6FF9FC148B11FD2AE06019D04CDAF0921A71F36013AA71F1FD2BE073AC01E32C11E01061ECD201221041E36003AA91ED602D6019FF53AF51D2FC148A11FD2ED06CD881CCDBE1821A81E36FFC313073AA91ED603C6FF9FF53E0A21A81E969F2FF53E05969FC148A1C148B11FD2130701B004CDAF093A5E1F21041EB6773AA81E3C32A41E3AA81EFE13C23007CDCF15C37D073AA81EFE14C240070E1ACD450EC37D073AA81ED6059F21051EA61FD2530721571F36013AA31EFE0AC27A07215D1F36013A631FFE00C26D0721631F36083A5F1FFE00C27A07215F1F3601CD6C1ACD121901061ECD20123AA91ED601C6FF9FF53AA81ED62CC6FF9FF53AA81ED60DC6FF9FC148A1C148B11FD2B00701BC04CDAF093AA81ED60DC6FF9F32A71FC3B3063AA31EFE0FC2CE070E1ACD450ECDCF153AA31EFE00C2DB070E00CD31193AA51E32CC1EC31405FB7621F21D7121F207E52A3802E9C921FB07E52A3602E93A0901C93EFACDA61D3EFACDA61DC91100000E03CD0500C91100000E01CD0500C921AB1E713AAB1EE67F5F16000E02CD0500C90E0DCD1C080E0ACD1C08C921AD1E702B71CD2E082AAC1EEB0E09CD0500C91100000E0CCD0500C91100000E0DCD0500C921AF1E712AAF1E2600EB0E0ECD0500C921B11E702B712AB01EEB0E0FCD050032AE1EC921B31E702B712AB21EEB0E10CD050032AE1EC921B51E702B712AB41EEB0E11CD050032AE1EC91100000E12CD050032AE1EC921B71E702B712AB61EEB0E13CD0500C921B91E702B712AB81EEB0E14CD0500C921BB1E702B712ABA1EEB0E15CD0500C921BD1E702B712ABC1EEB0E16CD050032AE1EC921BF1E702B712ABE1EEB0E17CD0500C921C31E702B712AC21EEB0E1ECD0500C911FF000E20CD0500C921C41E712AC41E2600EB0E20CD0500C92AC01E4DCD1F09C92AC11E4DCD1F09C921C61E702B712AC51EEB0E21CD0500C921C81E702B712AC71EEB0E22CD0500C921CA1E702B712AC91EEB0E24CD0500C921CB1E368011CB1E0E0ACD0500C91100000E0BCD0500C9216B1F702B712A6A1FEB0E1ACD0500C93E0CD3013E08D301DB010707071FDAAA09C39E09DB03E67FC921711F702B71CD2F092A701F444DCD39080E3ACD1C080E20CD1C083A4D1F32721F3A4E1F21721FBEDAF80921CC1E3A721FBED2F1092A721F260001CD1E094ECD1C0821721F34C2D00921CC1E3600013A02CD94083AAE1EFEFFCA110A013A02CDB308CD2E08C30E05C921771F732B702B712BD1C1702B71D53A771F3D32771FFEFFCA4E0A2A731FE52A751FC10A772A731F2322731F2A751F2322751FC3270AC9210000229D1E2A031E4DCD5E08CD370921781F36003AFB1D21781FBEDABE0A2A9D1EEB2A011E19444DCD860901061ECDC30832791FFE00CAAD0A3A791FFE01CA970A019402CDAF092A9D1E229F1EEB2A011E19361A3AFB1D32781FC3B70A1180002A9D1E19229D1E21781F34C2640A210000229D1ECD2F09C90E0721A11ECD6A1D7D3D327C1FFEFFC2DB0AC921000022A11E2A4B1E4DCD5E0801271ECD5F09217A1F36003A7C1F217A1FBEDA310B2AA11E01CA1F09227D1F2A7D1F444DCD860901271ECDD308FE00CA200B01A402CDAF091180002AA11E1922A11E217A1F34C2F30A3A651F1FD2C90B21000022A11E018000CD8609217A1F36003A7C1F217A1FBEDAC00B01271ECD3F09D600D6019F327F1F2A481E2322481E217B1F36003A7B1FD6809F217F1FA61FD2A20B2A7B1F2600018000093A7B1F11A11EE5CD341D01CA1F09C10A96D6019F327F1F217B1F34C36D0B1180002AA11E1922A11E3A7F1F1FDAB90B01B502CDAF09217A1F34C2490B01271ECDD308327F1F21000022A11EC921801F713A801FFE20DAF40B21F31D343E0021531FBED2F40B3A531F21F31DBED2F40BC93A030032811F2AA31E4D060021DD0C09095E2356EBE901FF1D11A11ECD8E1DDA190CCDC80A2AA11E01CA1F093A801F772AA11E2322A11EC3050DC33D0CC33D0CC33D0CC33D0CC33D0C01C202CDAF09C3050D2A801F4DCDE607C3050D2103003680C3710CC3050D21030036C0C3710CC3050D2103003680C3710CC3050D2A801F2600EB0E05CD0500C3050D2103003610C3A00CC3050D2103003620C3A00CC3050D2103003630C3A00CC3050D2A801F2600EB0E04CD0500C3050D2103003600C3CF0CC3050D2103003601C3CF0CC3050D2103003603C3CF0CC3050D2A801F2600EB0E02CD0500C3050D0A0C2E0C310C340C370C3A0C3D0C460C500C5B0C660C710C7F0C8A0C950CA00CAE0CB90CC40CCF0C3A811F320300C921821F713A821FFE09CA220D2A821F4DCDD00BC36E0D3A631FFE00C2340D2A821F4DCDD00BC36E0D3AF31D32831F21631F3A831FBEDA510D21631F3A831F9632831FC33A0D21831F3A631F96773E0021831FBED26E0D21831F350E20CDD00BC3590D3A821FFE0DC27B0D21F31D3600C921841F713A841FD600D6019F216C1FA6771FD2990D0E20CD0C0DC3A20D3A841FC6304FCD0C0DC921851F713A851FE6F81F1F1F1F4FCD7C0D3A851FE60F4FCD7C0DC921861F36013A5D1FD601D6019F326C1F3A6F1F8627326F1F3A6E1FCE0027326E1F3A6D1FCE0027326D1F2A6D1F4DCDA30D2A6E1F4DCDA30D2A6F1F4DCDA30D3A5D1FFE01C2120E0E3ACD0C0D0E20CD0C0DC3170E0E09CD0C0DC92AA11E7DE67F32891F1180FFCD441D22871FCDC80A2A871F01CA1F09E52A891FEBCD180A2A891F260022A11EC9218A1F713A551F1FD2590E3A8A1FFE0CC2590EC93AA61E1FD2A90E3A8A1FFE0CCAA90E3A5F1F328B1FFE00CA980E3A8B1FFE01C2800E218B1F363C3AF41D3C32F41D218B1FBEDA980E21F41D36000E0CCD0C0D3E00215D1FBED2A40ECDBE0D21A61E36003A511F1FD2CC0E3A8A1FD613D6019FF53AA31ED600D6019FC148A11FD2CC0ECD180EC93A8A1FFE0CC2D90E21F41D36002A8A1F4DCD0C0D3A8A1FFE0AC2ED0E21A61E3601C9218C1F713A8C1FD6619F2FF53E7A218C1F969F2FC148A11FD2110F3A8C1FE65F328C1F3A8C1FC9218D1F713A8D1FD6419F2FF53E5A218D1F969F2FC148A11FD2380F3A8D1FF620328D1F3A8D1FC93AA41E3D4F3E05B9DA6D0F3A571F21511FB6F5CD7D09C148A11FD26D0FCD1308FE1AC2640F3E1AC901D702CD39083E13C921901F36013A0300328E1F2AA41E4D060021451009095E2356EBE901FD1D119D1ECD8E1DDA970FCD4F0A2A9D1EEB2A011E197E328F1F2A9D1E23229D1EC36D10CDF307328F1FC36D10CD9609328F1FC36D102103003604C3E00FC36D102103003608C3E00FC36D10210300360CC3E00FC36D101100000E03CD0500E67F328F1FC36D10C30810C30810C30810C30810C30810C30810C30810C3081001E902CDAF09C36D102103003600C33210C36D102103003601C33210C36D102103003603C33210C36D1021901F36001100000E01CD0500328F1FC36D10880FAD0FB60FBF0FCA0FD50FE00FF00FF30FF60FF90FFC0FFF0F02100510081011101C10271032103A8E1F3203003A541F1FD292103AA31E328E1F21A31E36132A8F1F4DCD450E3A8E1F32A31E3A901F1FD2DC103A041E1FD2B2103AA71E3C32A71ED600D6019F32901FC3BD103A8F1FD60AD6019F32901F3A901F1FD2DC10CD7D091FD2DC10CD1308FE1AC2D6103E1AC9010003CDAF093A691F1FD2EB103A8F1FE67F328F1F3A641F1FD2FA102A8F1F4DCDEE0EC93A5B1F1FD209112A8F1F4DCD150FC93A8F1FC93E0021FA1DBED228113AFA1D3D32FA1DFE01C225113E0AC93E1AC93E0021F81DBED2461121F81D352B4E060021CD1E097E32911F21F71D34C9CD3C0F32911FFE1AC254113E1AC93E0021621FBED27D112A621F4DCDAD111FD27A113A621F32F71D21621F36003AF91D3C32F81DC3A9113E0021601FBED2A5112A601F4DCDAD111FD29E1121601F360021FA1D36023E0DC93A911FC9C3A9113A911FC9C32811C921921F713AF91D21921F86774F060021CD1E097E32931FFE1AC2D9112A921F260001CD1E093A911F773E01C921911F3A931FBEC2EA1121F91D34C3EF1121F91D36003E00C93A4E1F3C324E1F21CC1EBEDA03123E0DC92A4E1F260001CD1E094ECDEE0EC9CDF21132A81EFE20C21F12C31112C921961F702B7121F51D360021A91E36002B362021AA1E36003AAA1EFE20D253123AAA1EFE0BC24D1221A81E3600CD6714C33812CD11123A4E1F324D1F2AA81E4DCD38141FD27012CDC11521A91E3601C921941F360021971F36003E1921971FBEDA95122A971F260001501F09360021971F34C27A1221F61D360021F81D360023360023360021AA1E36002AA81E4DCD38141FDADA123AAA1EFE08DABE12C93AA81EFE2AC2CE120E08CD8714C3D112CD6714CDF21132A81EC3AA123AA81EFE3AC2BF133A941FFE00CAEB12C93AAA1EFE01C22C130E01CDA114D6413C32941F4F3E1AB9D20613C9CD11122AA81E4DCD38141FD229133AA81EFE5BC21F13CDB114214E1F3521A91E3604C9C3B1133AAA1EFE03CA3513C921A11F36FF2336003E1421A21FBEDAB01321A01F36003AA01F3C32A01F4F3E03919F2FF53AA01F21A11F864F060021540209E52AA01F4DCDA114E196D6019FC148A11FD27E13C34B133AA01FFE04C2A11321A91E3603CDF211FE5BC29613CDB114214E1F353AA21F32A81EC93AA11FC60332A11F21A21F34C23D13C93AA81EFE5BC2BC13CDB114C334143AAA1EFE00C2C813C921AA1E36083AA81EFE2EC20214CDF21132A81E4FCD38141FDA02143AAA1EFE0BDAEC13C93AA81EFE2AC2FC130E0BCD8714C3FF13CD6714C3D5133AA81EFE5BC20D14CDB114214E1F3521A91E36023A941FFE00C225143AFC1D3C32941F2A951F36000120002A951F093600C9C3A512C9219A1F71219B1F36003E0A219B1FBEDA64142A9B1F2600014902093A9A1FBEC25D143E01C9219B1F34C241143E00C93AAA1E3C32AA1E4F06002A951F093AA81E773AA81EFE3FC2861421F51D3601C9219C1F7121A81E363F219C1F3AAA1EBED2A014CD6714C39014C9219D1F712A9D1F2600EB2A951F197EC921F61D36013AC01E32C11ECDF21132A81E3AA81ED60DD6019FF53AA81ED65DD6019FC148B11FDABA153AA81ED641329E1F4F3E19B9D203153AA81EFE20C2FA14CDF21132A81EC30015010803CDAF09C3B7153AA81ED653D6019FF53AA81ED651D6019FC148B11FD249153A4E1F3C329F1FCDF21132A81ED61AD6019FF53AA81ED60DD6019FC148B11FDA4015C32215CDF21132A81EC38D15CDF21132A81ED630329F1F4F3E09B9D26315219F1F3601C38D15CDF21132A81ED63032991F4F3E09B9DA8D152A9F1F2600CD4F1DE52A991F2600C109EB219F1F73C363152A9E1F260001501F093A9F1F773A9E1FFE06C2B7153E1F219F1FBED2B115011603CDAF093A9F1F32C11EC3C214CDF21132A81EC93AA81EFE5FC2CE1521A81E363DC921A31F36003E2721A31FBEDAE9150E00CD450E21A31F34C2D415C921A91F702B712AA81FE51E0301301ECD180AC921AD1F722B732B702B712AAC1F7EFE24CA37162AAC1F3E7FA62AAA1FF53E7FA6C148B9CA26163E00C92AAA1F2322AA1F2AAC1F2322AC1FC307163E01C9CD0D1132A81E3A041E1FD25216019D1E119F1ECD8E1D9FC93AA81ED61AD6019FC921AF1F3601219C1E3600CD3D1732B51FFE3ACA9916219C1E36003AB51FFE1AC29316014803CD3908CD1308FE1AC28E163E01C9219C1E3600CD2D17C3651621B51F3600CDBB1732B41FFE00C2C716CD3D1732B41FFE1ACABA16CD2D17C3A9163AAF1F1FD2C4163E01C93E02C9CDC41722B71FCDBB1732B61F3AB41FD600C6FF9F21AF1FA61FD2F01621B41F35CDBB1732B31FC3D316CDAC1721B51F86FE00CA0217015C03CD1217CD2D173AAF1F1FD20F173E00C93E02C921B11F702B713AAF1F1FD22C1721AF1F36002AB01F444DCD3908C93AAE1F1FD23C1721AE1F3600CD180EC93AAF1F1FD27A17CD0D1132B21FFE13C2571721AE1F3601C344172A9C1E2600014C1E093AB21F773A9C1E3C329C1EFE4FDA7617012A03CD12173AB21FC93E1AC9CD3D1732B91FD6304F3E09B9DA92173AB91FD630C93AB91FD6414F3E05B9D2A417013A03CD12173AB91FD641C60AC9CD7D1787878787F5CD7D17C148B1C9CDAC1721B51F8677C9CDBB174F060060690E08CD641DE5CDBB17E1CD561DC9CD5B1632BA1F4F3E01B9DA32183ABA1FD601D6019F21581FA61FDA1C1821BB1F36013A9C1E21BB1FBEDA1C183ABB1F3D4F0600214C1E094ECD450E21BB1F34C2FC170E0DCD450E0E0ACD450E3ABA1FFE01C22F18C9C3DA17CD2E082A9C1E2600014C1E093624014C1ECD3908016B03CD3908CD2E08CD1308FE1AC25818C9C3DA17C9018F03CDAF09C92A4B1E4DCD5E08119E0301301ECDFD1532051E01301EC51E0301A41FCD180A3A301EE67F32301E3A311EE67F32311E01A203CDEA1501271ECDB30801271ECDE3083AAE1EFEFFC2B21801A503CDAF0921471E360021000022A11EC921FFFF229F1ECD37092A031E4DCD5E0801061ECD6E08CD2F093A611F2FF53A101E07C148A11FD2EC1821AE1E36FF3AAE1EFEFFC2FA1801B803CDAF0921261E360011C003010F1ECDFD1532041E2AFD1D229D1EC93E0021621FBED2211901C403CDAF093E0021601FBED2301901D403CDAF09C921BC1F713ABC1F1FD245193A131E32341EC34A1921341E36002AA11E7DE67FFE00CA611921341E340E1ACD450EC34A19CD1219CDC80A2A4B1E4DCD5E0801271ECD81083AAE1EFEFFC2821901E303CDAF0901A41FCDEA1521331E360001271ECD6E083AAE1EFEFFCAE9193A301E071FD2E3193A661F1FDAD519010104CD3908CD13084FCDEE0EFE59CAD219012304CD3908CD2E08013304CDEA1501271ECDB308C9CD2E083A301EE67F32301E01271ECD060901271ECDB30801271EC51E1001371ECD180A013604CDEA1501271ECDF608C90E0721FF1DCD6A1D3EFFCD411D2BEB21FB1D73C921CA1F22011E01004011FF1DCD931DDA311A21807F22FF1DC33C1A2AFD1DEB2AFF1D1922FF1DCD021AC901CA1F110600CD931D0E01E5CD6E1D01CA1F0922011E1100FFE1CD441D0E01CD6E1D22FD1D22FF1DCD021AC93AA41ED600C6FF9F21511FA632BD1F1FD2821ACD161A3A581F21571FB61FD2931ACDDA17C3A41ACD3A161FDAA41A2AA81E4DCD450EC3931A3ABD1F1FD2B11ACD180ECD401AC9CD401A3A4F1F324D1FCD6318CDBE1821BE1F36012336003E1921BF1FBEDA291B2ABF1F260001501F097EFE00CA221B3ABF1FD606D6019FF53ABF1FD60ED6019FC148B1F53ABF1FD611D6019FC148B1F53ABF1FD615D6019FC148B1F53ABF1FD616D6019FC148B11FDA221B21BE1F360021BF1F34C2C91A3ABE1F1FD25F1BCD161ACD6A1B1FDA591BCD4F0ACD6A1B1FD24D1B2A9F1E22A11EC3531B2AFF1D22A11ECDC80AC3331BCD401AC3621BCD6C1A2ABE1F4DCD3119C901FFFF119F1ECD931DB5C6FF9FC921000022C01F22C41FCD37092A031E4DCD5E08018000CD8609015C00CD940821000022C21F3AAE1ED6FFC6FF9F01C01F11C21FF5CD8E1D9FC148A11FD2C41B2AC21F2322C21FCDA708C39D1BCD2F093AAE1EFEFFC2E51B3E0011C41FCD9B1DB5C2E11B013904CDAF09CD2E08C92AC21F2322C01F3AAE1EE60387878787875F160021800019E51E1001271ECD180A21271E360021331E360001271EC51E1001061ECD180A3A311E072F21611FB61FD2451C2AC41F2322C41F3E01CD7A1DB5C23F1C014304CD3908CD491CCDB21AC3811BC9CD2E0821C61F36013E0B21C61FBEDA871C2AC61F260001271E097E32C71FFE20CA801C3AC61FFE09C2791C0E2ECD1C082AC71F4DCD1C0821C61F34C2511CC93E0021941FBED29B1C3A941F3D32031EC3A11C3AFC1D32031EC93AF61D1FD2AC1CCD5C183E0021941FBED2BF1C3A941F3D324B1EC3C51C3AFC1D324B1EC921C01E3AC11EBECAD11CC921031E3A4B1EBEC2DE1CCD5C18C9CD11123AA81EFE0DCAED1CCD5C18C921C91F702B71CD881CCDDF1C01061EC52AC81F444D1E21CD180ACDC61CC901061ECD20123AA91ED601D6019FF53AA81ED63DD6019FC148A11FDA2D1DCD5C183A4E1F324F1FC9EB5F1600EB1A856F131A8C67C95F16007BA56F7AA467C95E2356EB29E52929C109C95F16007BB56F7AB467C95E2356EB290DC2641DC95E2356EB7CB71F677D1F6F0DC26E1DC95F16007B956F7A9C67C94F06007B916F7A9867C969604E23461A916F131A9867C96F26001A956F131A9C67C9060C480DC2A91D3DC2A81DC9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>00D9 +A:DOWNLOAD STAT.COM +U0 +:C33304202020436F707972696768742028632920313937392C204469676974616C2052657365617263683F3F3F3F3F3F3F3F3F3F3F3F000000434F4E3A5244523A50554E3A4C53543A4445563A56414C3A5553523A44534B3A5454593A4352543A4241543A5543313A5454593A5054523A5552313A5552323A5454593A5054503A5550313A5550323A5454593A4352543A4C50543A554C313A522F4F00522F57005359530044495200522F4F20522F572053595320444952202A2A2041626F72746564202A2A004163746976652055736572203A004163746976652046696C65733A002020202000204472697665204368617261637465726973746963730036353533363A20003132382042797465205265636F7264204361706163697479004B696C6F6279746520447269766520204361706163697479003332202042797465204469726563746F727920456E747269657300436865636B656420204469726563746F727920456E7472696573005265636F7264732F20457874656E74005265636F7264732F20426C6F636B00536563746F72732F20547261636B00526573657276656420547261636B7300206973200054656D7020522F4F204469736B3A20643A3D522F4F0053657420496E64696361746F723A20643A66696C656E616D652E7479702024522F4F2024522F5720245359532024444952004469736B2053746174757320203A2044534B3A20643A44534B3A00557365722053746174757320203A205553523A00496F627974652041737369676E3A00203D004261642044656C696D6974657200496E76616C69642041737369676E6D656E74004261642044656C696D69746572003A200042797465732052656D61696E696E67204F6E2000522F002C2053706163653A2000496E76616C69642046696C6520496E64696361746F72002A2A20546F6F204D616E792046696C6573202A2A0046696C65204E6F7420466F756E64002053697A652000205265637320204279746573202045787420416363003635353336002073657420746F2000522F4F20496E76616C6964204469736B2041737369676E6D656E740057726F6E672043502F4D2056657273696F6E2028526571756972657320322E30290021000039223215215415F9CDED04FE20D24F04011104CDD204C38B04215D1536013A5C00D600D6019FF53A5D00D620D6019FC148A11FD27204CD330DC38B043A5C00FE00CA8004CD0214C38B04CD870A2F1FD28B04CDBA0D2A3215F9C9212215712A22152600EB0E02CD4C14C90E0DCD90040E0ACD9004C90E20CD9004C9212415702B712A23157EFE00CAD1042A23154ECD90042A231523222315C3B704C9212615702B71CDA0042A2515444DCDB104C91100000E0BCD4F14C91100000E0CCD4F14C9212815712A28152600EB0E0ECD4C14C9212A15702B712A2915EB0E0FCD4F14322715C9212C15702B712A2B15EB0E11CD4F14322715C91100000E12CD4F14322715C91100000E19CD4F14C9212E15702B712A2D15EB0E1ACD4C14C91100000E1BCD5214C91100000E18CD5214C91100000E1CCD4C14C91100000E1DCD5214C9115C000E1ECD4C14C91100000E1FCD5214221E15C911FF000E20CD4F14C9212F15712A2F152600EB0E20CD4C14C9213115702B712A3015EB0E23CD4C14C9CD7E052A1E1523234E210100CDBD14118000CD9E14225415C9215615712A56154DCDF604CDB305C9215815702B710E03215715CDC314EB2A2015193E07115715E5CD7214234DE1CDB214C9215F15702B7121601536003E03216015BEDA36062A60152600EB2A5E1519E52A6015260001591509D11ABECA2F063E00C921601534C209063E01C92A5D152600018000097EFE20C24F06215D1534C3390621611536003A6115FE04D2E6062A5D152600018000097E3262154F3E01B9D27A062A62154DCDEB06C37F060E20CDEB063E01216215969F2FF57ED62CD6019FC148B1F57ED63AD6019FC148B1F57ED62AD6019FC148B1F57ED62ED6019FC148B1F57ED63ED6019FC148B1F57ED63CD6019FC148B1F57ED63DD6019FC148B11FD2DF062A5D152600018000093601C3E306215D1534C35406215D1534C9216315712A61152600015915093A63157721611534C9216715722B732B702B7121681536013E00116615CDEA14B5CA72072A6415EB2A6615CD7F14216915732A6415EBCD81142264155059210A00CD7F14EB2266153E00CDD314B5C6FF9F216815A623F57E93D6019FC148A11FD26107CDAB04C36F0721681536003A6915C6304FCD9004C31007C9216D15722B732B702B712A6C15115415E5CD5A14EBE17323722A6C15EB010004CDE214DAB6072A6C15EB010004CDE214EB2B7323722A6A154E2346032B712370C38C07C9216E1571210000226F152271157D3275156F26002273150105002A1E1509EB017315CDDD14DA0E083A6E151FD2F1072A7315444DCDDB053275153A75151FDA0108117115016F15CD73071101002A731519227315D2CE072A6F15C901B901CDD204C901C701CDD204CD8A054F0600110A00CD010701D501CDD20421761536003E1F217615BEDA51082A7615260001771509360060692B34C2360801CF29CD4105012A01CD19053A2715FEFFCA93083A2715E60387878787874F060021CF29097E327615FEE5CA8D083A7615E61F4F0600217715093601CD2C05C35D0821761536003E1F217615BEDAC2082A76152600017715097E1FD2BB082A76154D0600110A00CD010721761534C29808C901E301CDD204CD3805C6414FCD90040E3ACD900401E801CDB1042A1E1523234E210100CDBD142297150105002A1E15094E2346032A9715EBCDA0142299153E00CDD314B5D6019F219715F5CDF814B5C6FF9FC148A11FD2250901FF01CDD204C32D092A9915444DCDC009010702CDB1040E00CDB707444DCDC009012002CDB1040107002A1E15094E234603CDC009013902CDB104010B002A1E15095E2356EB2929444DCDC009015402CDB1040104002A1E15097E3C6F2600118000CD9E14444DCDC009016F02CDB1042A9715444DCDC009017F02CDB1042A1E154E2346CDC009018E02CDB104010D002A1E15094E2346CDC009019D02CDB104CDA004C9219C15702B71CDA0042A9B15444D111027CD01070E3ACD9004CDAB04C9CD5A05229D15219F1536003E00119D15CDEA14B5CA180A2A9D157D1FD2060A2A9F154DCDCC05CDC3080E01219D15CDC314EB2B7323722334C3E809C921A215732B702B7121A415360021A615360036013AA21521A615BEDA840A21A515360121A31536003E0321A315BEDA720A2AA4152600EB2AA01519E52AA315260001591509D11ABECA690A21A515360021A415342B34C2410A3AA5151FD27D0A3AA615C921A61534C22D0A3E00C921AA153600CD39061E08013901CD190A32A715FE00C2A80A3AAA15D600C6FF9FC921AA15343AA715FE05C2180B3A030032A91521A81536002B36003E0321A715BEDA150B3AA71587874F060021390109444DCD690C01AD02CDB1043AA915E603878721A815864F060021590109444DCD690C3AA815C61032A8153AA915E6FE1F1F32A915CDA00421A71534C2C20AC3460C3AA715FE06C2AE0B01B202CDD20401C802CDD20401FA02CDD204011503CDD204012903CDD20421A71536003E0321A715BEDAAB0BCDA0043AA71587874F060021390109444DCD690C013803CDB10421A81536003E0C21A815BEDAA40BC3850B3AA815C60432A815D26B0BC3A40B0E20CD90043AA7158787878721A815864F060021590109444DCD690CC3770B21A71534C2430BC3460C3AA715FE07C2BF0BCD19083E01C9C3460C3AA715FE08C2CD0BCDDD09C3460C3AA7153D32A7158787878732A815CD39063A5915FE3DCAEF0B013B03CDD2043E01C9CD39062AA815260001590109444D1E04CD190A3D32A815FEFFC2140C014903CDD2043E01C921A91536FC3AA7153D32A715FEFFCA380C3AA915070732A9153AA815878732A815C3190C3AA915210300A621A815B6320300CD39063A5915FE20C2540C3E01C93A5915FE2CCA650C015C03CDD2043E01C9C38C0AC921AC15702B712AAB157EFE3ACA890C2AAB154ECD90042AAB152322AB15C36F0C0E3ACD9004C921AE15702B7121102722B11521B01536003E0011B115CDEA14B5CAFC0C2AAD15EB2AB115CD7F147B32AF152AAD15EBCD811422AD155059210A00CD7F14EB22B1153E00CDD314B5D6019F21B015B62BF57E93C6FF9FC148B11FD2F90C21B01536013AAF15C6304FCD9004C3A00C0E6BCD9004CDA004C9CD5105222015CD3805C6414FCD9004016A03CDB104C90E01CDB707444DCD8F0CC9016D03CDD204CD050DCD1B0DC9CD5A0522B315CD6C0522B51521B71536003E0011B315CDEA14B5CAA50D2AB3157D1FD2860D2AB7154DCDCC05CD050D018103CDB1042AB5157D1FD2780D0E4FCD9004C37D0D0E57CD9004018403CDB104CD1B0D0E0121B315CDC314EB2B7323720E0121B515CDC314EB2B7323722334C3440DCDA004C93A5C00FE00CAB90D3A5C003D4FCDCC05C9CDB305CDA90D211D15360021CC2936FFCD6C131FD2E40D3ACC29FE00C2DA0DC93ACC293D32CC29C3F00D3A5D00FE20C2F00DCD260DC921000022B8157D325C00216800363F216A00363F015C00CD19053A2715FEFFCA43103A2715E6038787878787C6806F260022BA2921CB29360021000022BE293ACB292F01B81511BE29F5CDDD149FC148A11FD2980ECD5D1321C62936013E0B21C629BEDA8E0E2AC6292600EB2ABA2919E52AC6292600EB2ABC2919C10ABECA790E21C629360BC3840E3AC629D60BD6019F32CB293AC6293C32C629C24D0E2ABE292322BE29C32F0E3ACB291FD2A90E2ABE292B22BE29C3600F2AB81522BE292322B815CD5D1311000221B815CDF8149F1110002ABC2919EB210600F5CDF8149F2FC148B11FD2ED0E01A503CDD20421000022BE2921010022B815CD5D132ABE2901BA152909E52ABE29EBE173237221C62936003E0B21C629BEDA2E0F2AC6292600EB2ABA2919E52AC6292600EB2ABC2919C10A773AC6293C32C629C2030F2ABE2901BA1929093E00772336002ABE2901BA1D2909772336002ABE2901BA212909772336002ABE2901BA252909772336002ABE2901BA1929094E2346032ABE29C501BA192909C17123702ABE2901BA252909010F00E52ABA29097ED1CD6514010C00E52ABA2909010400E52A1E15097EE1A66F2600118000CD9E14C109E52ABE2901BA252909C171237021C72936010105002A1E15093EFFCDF514D2D20F21C729360221C62936103E1F21C629BEDA3D10C3F10F3AC72921C6298677D2D70FC33D102AC6292600EB2ABA29197E32C8293AC729FE02C21C102AC629260001010009EB2ABA29193AC829B632C8293AC829FE00CA3A102ABE2901BA212909E52ABE2901BA1D2909EBC1CD7307C3E30FCD2C05C30A0E3E0011B815CDEA14B5C2581001BA03CDD204C35C133ACC29FEFFC293123E0121B815CDF514D2641121010022C0293E0021C029CDF514D2641121000022C02921000022C4292AB8152B2BEB21C429CDF814DA61112AC42901BC1529095E2356EB22BE29CD5D132ABC2922BA292AC42901BA1529095E2356EB22BE29CD5D1321C62936013E0B21C629BEDA54112AC6292600EB2ABA29197E32C929E52AC6292600EB2ABC29197E32CA294FD11AB9D23B112AC42901BA1529095E2356EB22C2292AC42901BC152909E52AC42901BA152909E34E2346E17123702AC42901BC152909E52AC229EBE17323722AC0292322C02921C629360BC34A113ACA2921C929BED24A1121C629360B3AC6293C32C629C2C6101101002AC4291922C429D28810C371103A1D151FD2741101C903CDD204C37711CDA00401D003CDB10421000022C02901B81511C029CDDD14D28D122AC02901BA1529095E2356EB22BE29CD5D13CDA0042E10E52ABC29444D115C00E10A1203132DC2B011215C0036003A1D151FD2E911015C00CDA3053A7F00FE00CADB1101E603CDB104C3E6112A7D00444D111027CD0107CDAB042ABE2901BA2529094E2346111027CD0107CDAB042ABE2901BA2129094E2346111027CD01070E6BCD9004CDAB042ABE2901BA1929094E234611E803CD0107CDAB040E52CD90040E2FCD90040109002ABC29097E071FD249120E4FCD9004C34E120E57CD9004CDAB04CD3805C6414FCD90040E3ACD9004010A002ABC29097E0732C8291FD274120E28CD9004CDC0133AC8291FD283120E29CD90042AC0292322C029C38311CD260DC35C1321000022C02901B81511C029CDDD14D25C13CDE4041FD2B012CD1208C92AC02922BE29CD5D13CDA004CDC0132ACC294D060021171309095E2356EBE90109002ABC29093E80B62ABC290977C31F130109002ABC29093E7FA62ABC290977C31F13010A002ABC29093E80B62ABC290977C31F13010A002ABC29093E7FA62ABC290977C31F13CF12E112F31205132E10E52ABC29444D115C00E10A1203132DC22B13215C003600CD750501EC03CDB1043ACC2987874F060021990109444DCDB1042AC0292322C029C39912C92ABE292929292911CF291922BC29C93A6D00FE20C277133E00C92E04115915016E000A1203132DC27F133A5915D653D6019FF53A5A15D620D6019FC148A11FD2A713211D1536013EFEC91E0401A901CD190A32CC29FE00C2BD13018E03CDD2043E01C921CD2936013E0B21CD29BEDA01142ACD292600EB2ABC29193E7FA632CE29FE20CAF7133ACD29FE09C2F0130E2ECD90042ACE294DCD90043ACD293C32CD29C2C513C9CD3906CD39063A5915FE3DC22F14CD390601F503CDFE051FD22614CDA90DCD6305C32C1401F903CDD204C34814CDA90D1E08013901CD190AFE08C24514CDC308C34814CDBA0DC9C30000C30500C30500CD0500C9C9C969604E23461A816F131A8867C9EB5F1600EB1A856F131A8C67C9EB5F1600EB1AA56F131AA467C9444D2100003E10F529EB9729EB8D916F7C986713D29814091BF13DC28614C9444D2100003E1029EB29EBD2AD14093DC2A514C97E070DC2B314C95E2356EB290DC2BD14C95E2356EB7CB71F677D1F6F0DC2C714C95F16007B956F7A9C67C969604E23461A916F131A9867C96F26001A956F131A9C67C95F16007B965F7A239E57EBC91A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A>80DD +A:DOWNLOAD SUBMIT.COM +U0 +:C3DF0120636F7079726967687428632920313937372C206469676974616C207265736561726368200D0A244572726F72204F6E204C696E6520245355424E6F2027535542272046696C652050726573656E74244469736B205772697465204572726F7224436F6D6D616E6420427566666572204F766572666C6F7724436F6D6D616E6420546F6F204C6F6E6724506172616D65746572204572726F7224496E76616C696420436F6E74726F6C20436861726163746572244469726563746F72792046756C6C2443616E6E6F7420436C6F73652C20526561642F4F6E6C793F242100003922F00521930EF9CDCC02CD8A03CDFE04CD8705C921DD05702B712ADC05EB0E09CD8A05C921E005702B712ADF05EB0E0FCD8D0532DE05C921E205702B712AE105EB0E10CD8D0532DE05C921E405702B712AE305EB0E13CD8A05C921E605702B712AE505EB0E14CD8D05C921E805702B712AE705EB0E15CD8D05C921EA05702B712AE905EB0E16CD8D0532DE05C921EF05732B702B712BD1C1702B71D53AEF053D32EF05FEFFCAA6022AEB05E52AED05C10A772AEB052322EB052AED052322ED05C37F02C921F305702B71012801CDF701012B01CDF70101B605CDF7012AF205444DCDF7012AF005F9C9018100C51E7F01F405CD70022A8000260001F405093600013A01C51E03016500CD7002015C00CD07023ADE05FEFFC20303013D01CDA7022174063680C93E7F217406BED22503015C00CD3D02FE00CA20033E1AC921740636003A74063C3274063D4F0600218000097E327506FE0DC262033AB8053C32B8054F3E39B9D2620321B80536302B7E3C774F3E39B9D2620321B70536302B343A7506D661FE1AD274033A7506E65F3275063A7506C901BB05CD4D02FE00CA8903015301CDA702C9217606360021000022760E217C0E36013A7C0E1FD2800421780E3600CD0903327D0ED61AC6FF9FF53A7D0ED60DC6FF9FC148A11FD26B043A7D0EFE0ACA68043A7D0EFE24C23604CD0903327D0EFE24C2E6032A7D0E4DCDC404C333043A7D0ED630327D0E4F3E09B9D2FE03018D01CDA702C33304217A0E3600CDAD043A7D0EFE00CA2204217D0E35CD81041FD21C04C31204CDAD04C30604CD81041FD233042A790E4DCDC404C32204C368043A7D0EFE5EC26104CD0903D661327D0E4F3E19B9D25604019D01CDA702C35E043A7D0E3C4FCDC404C368042A7D0E4DCDC404C3A6033A7D0ED60DD6019F327C0E2A780E4DCDC404C39A03C92A7A0E260001F405097E32790ED620D6019FF53A790ED600D6019FC148B11FDAAA04217A0E343E01C93E00C92A7A0E260001F405097EFE20C2C304217A0E34C3AD04C9217B0E712A760E2322760E11FF07CD9905D2DE04016401CDA7022A760E017606093A7B0E773A780E3C32780E4F3E7DB9D2FD04017C01CDA702C901BB05CD2D0221DB05360001BB05CD5D023ADE05FEFFC21D0501B701CDA702CD7A05327E0EFE00CA65053A7E0E3280004F06002181000936002A7E0E26000182000936243E00217E0EBED25F05CD7A052A7E0E26000180000977217E0E35C34205CD7803C31D0501BB05CD1A023ADE05FEFFC2790501C601CDA702C92A760E2B22760E017606097EC9C30000C30500C30500CD0500C9C9C95F16007B956F7A9C67C90000000000000000000000000000000000000000000030303120240024242420202020205355420000001A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A>00E1 +A:DOWNLOAD DDT.COM +U0 +:01BC0FC33D01434F505952494748542028432920313938302C204449474954414C205245534541524348202020202020444454205645525320322E3224310002C5C51130010E09CD0500C12107007E3D90571E00D521000278B1CA65010B7E121323C35801D1C1E56278B1CA87010B7BE607C27A01E37E23E36F7D176FD283011A841213C36901D12E00E90E10CD0500325F1EC921661E702B712A651EEB0E11CD0500325F1EC91100000E12CD0500325F1EC921681E702B712A671EEB0E13CD0500C9216A1E702B712A691EEB0E14CD0500C9216C1E702B712A6B1EEB0E15CD0500C9216E1E702B712A6D1EEB0E16CD0500325F1EC921701E702B712A6F1EEBC38306000000C34F03C324052A731EEB0E1ECD0500F579CD8F06F1C9FE20C8FE09C8FE2CC8FE0DC8FE7FCA2405C90E0DCD15000E0ACD1500C9CD8C06FE0DCA1805CD1C00CA39000E04217A063620230DC24C000E05217A0677CD8C06CD1C00CA6A00230DCA1805C358003A7A06FE20C9D630FE0AD8C6F9FE10D8C31805CD98063DC21805EB4E2346790504C9CD7D00C21805C9171717E638C917171717E630C9EB2A7A06EB7BBEC2AF00237ABEC82B2B2B0DC2A5000DC90604D5117A061ABEC2CA00231305C2BD00D1C92305C2CA0011F8FF19D1130DC2B7000DC9C5CD3900CA18050E08216006CDA000C218050D79C1C9C5CD3900CA18050E05217206CDB700C218050D79C1C9CDF100FE04CA1805C9CDF100FE03CA1805FE04C03DC9217A06117B060E021A7723130DC225011AFE20C21805772150060E08CDA000C218050D79CD9300C9CD1D01F5CD7D00F1F6C0C91A2A11007723221100C9CD8C06FE0DCA4005FE2ECA4005CD3C00CA18050E1121A605114505CDB700C27E01C350010E0A21CE05CDB700C29201CD5001CD8C00C351010E0621E605CDB700C2AA01CD5001CD7D00CD510178C351010E0121EA05CDB700C2C601CDDB00CD9300470E40CDDB00B1B0C351010E08210A06CDB700C2DC010D79CD9300470E80C3BE010E02211206CDB700C2F4010C0C0CCDDB00CD9300B1C351010E01211606CDB700C21002CDDB00CD9300F606CD5101CD8C00C351010E06212E06CDB700C2360279FE04DA2302C60547CD0701CD9900B0CD5101E6CFFE01C0C3A0010E01213206CDB700C25102CD8C00FE08D21805CD9300F6C7C351010E02213E06CDB700C271020DC265020EC1C367020EC5CD1001CD9900B1C351013A7A06FE4AC28102CD4501F602C38B02FE43C29602CD4501F604CD510179CD510178C35101FE52C21805CD1D01F6C0C351012A0E00D5EB2A0C007B957A9CD2B7022A1300F9C9D17E23220C00C93CE607FE06DAC802C603FE05DACF02C602C6414FC3150047E6F00F0F0F0FC69027CE40274FCD150078E60FC69027CE40274FC3150006044ECD15002305C2F5020E20C315007AE6380F0F0FC9CD0303874F214206094ECD1500234ECD15000E20CD1500C31500CD0303E606FE06C2BE020E53CD15000E50C31500CD2E002A0C007CCDD5027DCDD5020E20CD1500CD1500C9210000392213003A1000B7CA710321FFFF220E003CC271033C3210002A0C00C39703CD9E06C240052110007EB7CA830335CA40052A0C00CDA106CD2E000E20CD1500CD1500CD3B03CDA30257214505011100BECAFD04230DC2A1030E0ABECAE904230DC2AC030E06BECACE04230DC2B703E6C0FE40CAB404FE80CAA5047AE6C7D604CA96043DCA90043DCA7C047AE6C0CA4A047AE607CA3F04D602CA3404D602CA2904D603CA1A047AE608C20B057AE6074F3D21390609CDF302CD0303FE06C29F04213606CDF302C37103213206CDF302CD0303CDD502C371030E43CD1500CD0A03C3D9040E4ACD1500CD0A03C3D9040E52CD1500CD0A03C37103211A067AE607CA0B057AE60F3DCA6E04FE03DA6104D60587874F09CDF302CD2403C37103CDF302CD24030E2CCD1500C3D904211606CDF302CD0303CDBE020E2CCD1500C3F404211206C39904210E06CDF302CD0303CDBE02C371037AE6380F4F21EE0509CDF302C3C50421EA05CDF302CD0303CDBE020E2CCD15007AE607CDBE02C371037987874F21CE0509CDF302CDA302F5CDA30257F15FCD9506C371037987874F21A60509CDF302CDA302CD9206C371037987874F21620509CDF302C37103217606CDF3027ACD9206C37103CD2E000E3FCD15002A1300F921000039221300CD3803221100CD8906CD5A012A1100220C00C32B052A1300F9C900070F171F272F373F76C9E3E9EBF3F9FBC6CED3D6DBDEE6EEF6FE222A323AC3CD454920205350484C44492020584348475043484C5854484C52455420484C5420434D432053544320434D4120444141205241522052414C2052524320524C43204E4F5020435049204F52492058524920414E492053424920494E2020535549204F555420414349204144492043414C4C4A4D50204C444120535441204C484C4453484C444D4F562041444420414443205355422053424220414E4120585241204F524120434D5020494E5220444352204D5649204C58492053544158494E5820444144204C444158444358205253542050535720504F5020505553484E5A5A204E434320504F504550204D20422043204420452048204C204D20412042202020442020204820202053502020505357203F3F3D201E4D06002145C3A206C3AA06C3CF0DC3B60BC3DD0BC3C70BC3050CC32D0CC3900CC3660CC31F0CC9E3224A0FE3C300002A060022A80621A206220100210000220600AF324F0F210001220C00225D0F22870F22B90F21000131B70FE5210200E52B2B22B70FE5E5224D0F3EC33238002186062239003A5D00FE20CAFE06210000E5C3AD0931AF0FCD9309DA0D07218006220600CD150C3E2DCDC70BCDB60BCDDD0BFE0DCAFE06D641DAAB0BFE1AD2AB0B5F160021370719195E2356EBE97E07AB0BAB0BC607AB0B5C087008DA080409AB0BAB0B97075A09AB0BAB0BAB0BAB0B9C097A0AC30ABF0AAB0BAB0BE70AAB0BAB0BE5D5C5AF325B000E0F115C00CDA206C1D1E1C9CD9309D2AB0BCD900C3DC2AB0BCD660C220C00CD0900C3FE06CD9309D2AB0BCD900CCABB07CD660C220C003DCABB07CD660C220E003DC2AB0BAFC3BD073E0C321000CD0600C3FE06CD900CCAE507CD660CDAD507225D0FE67F3DCAE507CD660C3DC2AB0BC3F0072A5D0F7DE6F06F11BF0019225F0FCD150CCD1F0CC2FE062A5D0F22610FCD2E0CCDC50B7ECD050C23CD450CDA19087DE60FC20508225D0F2A610FEBCDC50B1ACD360C132A5D0F7D93C223087C92C223082A5D0FCD450CDAFE06C3F307CD900CFE03C2AB0BCD660CE5CD660CE5CD660CD1C1C97B917A98C9CD41087CB7C2AB0BCD5708DAFE067D0203C36408CD150CCD900CCD660CE5CD660CE5CD660C444DD1E1F3CAA108DA8F0822B90FE67F3DCAA108CDB2083DCAA1085950CDB20831AF0FD1C1F1E1F92AB90FE52AB70FFBC9F5C5214F0F7E34B7CACD08237E234623BBC2CD0878BAC2CD087E1223732372231A773EFF12C1F1C9CD900CFE02C2AB0BCD660CE5CD660CD1E5CD150C19CD2E0CCDC50BE1AF956F3E009C6719CD2E0CC3FE06AF327C00325C00CDDD0B0E09215D0077230DCAAB0BCDDD0BFE2ECA2609FE0DC213090DCA3009362023C326090E04FE2EC24B09216500CDDD0BFE0DCA4B0977230DCAAB0BC33A090DCA5509362023C34B093600C3FE06CD4108CD5708DAFE060A037723C35D092165007EE67FFE48C0237EE67FFE45C0237EE67FFE58C9EB2A870F7D937C9AEBC9CD8109D022870FC9E5210000CD8109E1C9CD900C210000CAAC093DC2AB0BCD660CE5CD6B07FEFFCAAB0BCD6A09CAE109E111000119E5115C000E14CDA206E1B7C2460A1180000E801A1377230DC2D309CD8B09C3C009CD740BFE1ACAAB0BDE3AC2E10957E1E5CD260A5FCD260AF5CD260AC14F097BB7C20C0A606922B90FC3460ACD260ACD260A77231DC20F0ACD260AF5CD8B09F1C2AB0BC3E109C5E5D5CD740BCD590C07070707E6F0F5CD740BCD590CC1B047D1825778E1C1C90E0CCDA206216F0A7EB7CA5A0ACDC70B23C34E0ACD150C2A870FCD2E0CCDC50B2AB90FCD2E0CC3FE060D0A4E4558542020504300CD900C3DC2AB0BCD660CCD150CE5CD2E0CCDC50BE17EE5CD050CCDC50BCDB60BCDDD0BE1FE0DCABB0AFE2ECAFE06E5CD930C3DC2AB0BCD660C7CB7C2AB0B7DE17723C3840AAFC3C50A3EFF324C0FCD900C210000CADE0A3DC2AB0BCD660C7DB4CAAB0B2B224D0FCD440DC38508CDDD0BFE0DC2F50ACD440DC3FE06010B0021B30DBECA080B23040DC2FB0AC3AB0BCDDD0BFE0DC2AB0BC5CD150CCD1A0DCDC50BCDB60BCD900CB7CAFE063DC2AB0BCD660CC178FE05D2590B7CB7C2AB0B7DFE02D2AB0BCDE30C67413EFECD530BA441677DCD530BB412C3FE0605C807C3530BC2690B7CB7C2AB0B7D21B40F77C3FE06E5CD010DD1732372C3FE06E5D5C53A5B00E67FCA940B16005F218000197EFE1ACAA60B215B0034B7C3A70B0E14115C00CDA206B7C2A60B325B00C37F0B37C1D1E1C9CD150C3E3FCDC70BC3FE060E0A11650FCDA20621670F22630FC93E20E5D5C55F0E02CDA206C1D1E1C9FE7FC8FE61D8E65FC9E521660F7EB73E0DCAF40B352A630F7E2322630FCDD40BE1C9FE0AD2000CC630C3C70BC637C3C70BF51F1F1F1FE60FCDF60BF1E60FC3F60B3E0DCDC70B3E0AC3C70BC5D5E50E0BCDA206E601E1D1C1C9EB7CCD050C7DC3050CFE7FD2400CFE20D2C70B3E2EC3C70BEB2A5F0F7D936F7C9AEBC9FE0DC8FE2CC8FE20C9D630FE0AD8C6F9FE10D8C3AB0BEB5E235623EBC9EB210000CD590C29292929B56FCDDD0BCD500CC2710CEBC973237223E521560F34E1C9CDDD0B21560F360023FE0DCAD50CFE2CC2AE0C3E8032560F110000C3B10CCD6D0CCD850CFE0DCAD50CCDDD0BCD6D0CCD850CFE0DCAD50CCDDD0BCD6D0CCD850CFE0DC2AB0B11560F1AFE81CAAB0B13B7070FC9E521C30D581600194E21B30F7EEBE1C9CDE30C0DCAFE0C1FC3F60CE601C9D60621BE0D5F1600195E16FF21BB0F19C9CD010D5E2356EBC97ECDC70B78FE05D22B0DCDF30CCDF60BC9F53E3DCDC70BF1C23D0D21B40F7ECD050CC9CD120DCD2E0CC921B30D0600CD150CC5E5CD1A0DE1C1042378FE0BD2660DFE05DA4C0DCDC50BC34C0DCDC50BCD850EF5D5C5CD9309D2860D2AB90F220C0021100036FFCD0600C3AF0D2B225F0F2AB90F7ECD050C23CD450CDAAF0DF5CDC50BF1B3CAAB0D5E2356EBCD2E0CC3AF0D7ECD050CC1D1F1C9435A4D4549414244485350F6F4FCFAFE0107080305210000224D0FC9F322B70FE12B22B90FF521020039F131B70FE5F5C5D52AB90F7EFEFFF5E5214F0F7E3600B7CA040E3D47235E2356237E1278C3F30DE1F1CA280E2322B90FEB21A8064E2346CD5708DA280ECDC80D2A4A0FEB3E82B737C38508FB2A4D0F7CB5CA4E0E2B224D0FCD1F0CC24E0E3A4C0FB7C2480ECD850EC38508CD440DC38508CDC80D3E2ACDC70B2AB90FCD9309D2620E220C00CD2E0C2AB70F225D0FC3FE06110D00212F0F7EA023BE23CA810E141DC2740E5A1600C92AB90F4623E5CD6E0E21490F73219C0E19195E2356EBE9B80EE00EB80EE00EBE0EF20E040F260F260F230F230F190F260F140FCDCE0EC2290FCDD90EC3290F3AA806BBC03AA906BAC9C1E15E235623E5C5C3C40E2AB50F5E2356C9CDCE0ECAED0EC1C53E02C32B0FD1D5C3290F78FEFFC2FC0EAFC32D0FE6385F1600C3290F2AB70FEBCDC40EC2290FC3BE0EC3290FD1D5C3290FCDD90EC1C53E02C32B0FD113D5D113D53E013C37E1C9FFC3C7C2FFCDC7C4FFC9C7C7FFE9C706C7C6CF01E722C7C0F7D303CD3908CD2E08CD1308FE1AC25918C9C3DB17C9018F03CDAF09C92A201D4DCD5E08119E0301E11DCDFD1532B61D01E11DC51E0301551FCD180A3AE11DE67F32E11D3AE21DE67F32E21D01A203CDEA1501D81DCDB30801D81DCDE3083A5F1EFEFFC2B31801A503CDAF0921F81D360021000000209000400008211092102112424800091002400010400808410200824248090920422101200822121110108842484924249242492442492084248410920910924849092082440424909084910848909084844424242048080408080004002084200842109204212444240488222492422490924408410821021011104108420808909249248490921248204201249092490924924808924908240891048112481124892120249249200000000000000000000000000000000000000000000000000000000000000000000000000000002492492484012481049208021010020492410921084100AAAAAAAAAAAAA0004092249249248922212492482449002492491120922221092490911002124124888092091090110208042000084884480090040090844102084122084920400000080442420891092000840024908411101092412224120090002424492492000892241248211121022120890892424822092112491120420902042090888810020010101104042424900100008110902108020842020044210880000004001009200824042104909242490904020204440401080412408911221080212490492424488910812200000004210081010200108882490110892449248492492410108104880555555524921000902408411012249090200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>0020 +A:DOWNLOAD DISPLAY.COM +U0 +:C32101446973706C6179205665722E312E302C204F63742E2C33302C31393830243159082E21118807015C000A1203132DC22C01CD5F03018807CD97023A5B07FEFFC24E01010008CD4C03CD0D072A7A07444DCD8703CD6705CDC3063A7407FE0DC26D01CD6705C35901C385010E42CDEF061FD27901C334010E45CDEF061FD28501CD0D0721770736002101002284073A7407FE2DC2A0012177073601CD9F063A7407D6303278074F3E09B9DAE7012A78072600228407CD9F063A7407D6303278074F3E09B9DAE7012A8407292929E52A840729C109E52A78072600C109228407CD9F06C3BA013A7407FE4CC2FD012A77074D2A8407EBCD5004C34D023A7407FE50C237023E00118407CD5007B5CA2B021117002A8407CD2F072284072A77074D2A8407EBCD5004C334022A7A07227E07228607C34D023A7407FE44C24A022A8407444DCD9505C34D02C35302CD6705C35901011008CD4C03C35901FB761100000E01CD0A07C9215C07712A5C072600EB0E02CD0A07C9215D07712A5D072600EB0E05CD0A07C9215F07702B712A5E07EB0E0ACD0A07C9216107702B712A6007EB0E0FCD0A07325B07C9216307702B712A6207EB0E10CD0A07325B07C9216507702B712A6407EB0E11CD0A07D6FFC6FF9FC9216707702B712A6607EB0E13CD0A07C9216907702B712A6807EB0E14CD0A07325B07C9216B07702B712A6A07EB0E15CD0A07325B07C9216D07702B712A6C07EB0E16CD0A07C91100000E19CD0A07C9216E07712A6E072600EB0E0ECD0A07C9217007702B712A6F07EB0E1ACD0A07C90E0DCD67020E0ACD6702C9217207702B712A7107EB0E09CD0A07CD4103C92173073600215908227A07227E072280072A7A07228607219407360021A80736002A0600227C07C9212B08702B713A73071FDA4F042A2A08222C08117C07CD5307EB218000CD1007EB222E08215B0736003E00112E08CD5007B5C6FF9FF53A5B07D600D6019FC148A11FD2EE032A2C08444DCD3103018807CDE2021180002A2C0819222C082A2E082B222E08C3B0033A5B07FE00CAFB0321730736FF2A2C08228007018000CD31033A73071FD22F04010001112C08CD4807EB2B7323722A2C087EFE1ACA2F042A2C0823222C08C31C042A2C082B222C082A2C087EFE0ACA49042A2C082B222C08C336042A2C08228207C9213208722B732B713A3008FE00C29B043E00113108CD5007B5C6FF9F018207117E07F5CD43079FC148A11FD298042A7E077EFE0AC28E042A31082B2231082A7E0723227E07C36004C3ED042A3108232231083E00113108CD5007B5C6FF9F117A07017E07F5CD43079FC148A11FD2DA042A7E077EFE0AC2D0042A31082B2231082A7E072B227E07C3A204117A07017E07CD4307D2ED042A7E0723227E072A7E07228607C92134083600018207117E07CD4307D266052A7E077E323308FE0ACA5A053A3308FE09CA27052A33084DCD670221340834C350053A3408E6074F3E079132360821350836003A3608213508BEDA50050E20CD6702213408342334C238052A7E0723227E07C305052A7E0723227E070E0ACD6702C92A8607223808227E0721370836013E17213708BEDA8805CDF40421370834C275052A7E072286072A3808227E07C9213B08702B713E00113A08CD5007B5C2BC05017E07118007CD43072240082A7E07223E08C32B062A7A07223C083E00113A08CD5007B5CA19062117002242083E00114208CD5007B5CA0F062A3C087EFE0AC2F0052A42082B2242082A3C0823223C08118207CD5307D20C06210000224208210100223A08C3D4052A3A082B223A08C3C205013C08118007CD43072240082A3C08223E082A4008E52A3E08444D2A7A07EBE10A1203132B7CB5C23906017A07113E08CD4307EB23732372113E08017E07CD4307D26E06014008117E07CD4307EB2B732372C374062A7A07227E072A7E07228607014008118007CD4307EB2B732372014008118207CD4307EB2B7323722A8007444DCD8703C92A7507260001A907097E3274074F3E5AB9D2BB063A7407E6DF3274073A75073C327507C90E2ACD670201A907CD87020E0ACD67023AAA07327607FE00C2E606217407360DC3EE062175073602CD9F06C9214408712144083A740796D6019FF53A7607D601D6019FC148A1C9C30500C30000444D2100003E10F529EB9729EB8D916F7C986713D22907091BF13DC21707C9444D2100003E1029EB29EBD23E07093DC23607C969604E23461A916F131A9867C96F26001A956F131A9C67C96E06014008117E07CD4307EB2B732372C374062A7A07227E072A7E07228607014008118007CD4307EB2B732372014008118207CD4307EB2B7323722A8007444DCD8703C92A7507260001A907097E8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002046696C65206E6F7420666F756E642420496C6C6567616C20636F6D6D616E64241A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A>00E5 +A:DOWNLOAD DUMP.COM +U0 +:21000039221502315702CDC101FEFFC21B0111F301CD9C01C351013E80321302210000E5CDA201E1DA5101477DE60FC24401CD7201CD59010FDA51017CCD8F017DCD8F01233E20CD650178CD8F01C32301CD72012A1502F9C9E5D5C50E0BCD0500C1D1E1C9E5D5C50E025FCD0500C1D1E1C93E0DCD65013E0ACD6501C9E60FFE0AD28901C630C38B01C637CD6501C9F50F0F0F0FCD7D01F1CD7D01C90E09CD0500C93A1302FE80C2B301CDCE01B7CAB30137C95F16003C321302218000197EB7C9AF327C00115C000E0FCD0500C9E5D5C5115C000E14CD0500C1D1E1C946494C452044554D502056455253494F4E20312E34240D0A4E4F20494E5055542046494C452050524553454E54204F4E204449534B24EA21F5C5D5E5590E02CD0610E1D1C1F1C9FE20C8FE09C8FE2CC8FE0DC8FE7FCA3C05C90E0DCD15000E0ACD1500C9CD0907FE0DCA2A05CD2400CA41000E04218B063620230DC254000E05218B0677CD0907CD2400CA7200230DCA2A05C360003A8B06FE20C9D630FE0AD8C6F9FE10D8C32A05CD4100CA2A05110000010000218B06097EFE20CAB000CD78006B62292929295F160019EB0379FE04C29100424B7B0504C9CD8500C22A05C921050036C336C3210000220600C9171717E638C917171717E630C9EB2A8B06EB7BBEC2E700237ABEC82B2B2B0DC2DD000DC90604D5118B061ABEC20201231305C2F500>001A +A:DOWNLOAD ED.COM +U0 +:C3C00120434F505952494748542028432920313937392C204449474954414C205245534541524348204449534B204F52204449524543544F52592046554C4C2446494C45204558495354532C204552415345204954244E45572046494C45242A2A2046494C4520495320524541442F4F4E4C59202A2A242253595354454D222046494C45204E4F542041434345535349424C452442414B24242442414B2424242D28592F4E293F244E4F204D454D4F525924425245414B202224222041542024316D1A014D1D110600CDF219226D1A0E0ACDD7192BEB21381B737E3C4F060060690E08CDCD1922391B1100042A391B19116D1ACDFD19D2020201A801CD0A0CCD000001391B116D1ACDED192BEB2B7323720E0121391BCDD319EB2B73237201391B110600CDED19223B1B01391B113B1BCDED19225E1B2A6D1A014D1D0936002A6D1A2B226F1A0E01216F1ACDD319EB2373237221101D360021661B360021141C36013A5D00D620D6019FF53A6D00D620C6FF9FC148B11FD27D02CD2D0D3A5C0032641BFE00C29102CDDE0C32641BC39D023A641B3D32641B215C0036003A6C0032651BFE00C2B1023A641B32651BC3B8023A651B3D32651BCD450ECD550D214D1D360A21010022221D2A6F1A22241D21681B3600C31A03316D1A21061D3623C3F502316D1A21061D363FC3F502316D1A21061D363E316D1A21661B360001B201CD0A0C2A061D4DCDCB0B01BA01CDFA0B2A211D4DCDCB0BCDEF0B316D1A210D1D360121071D3600210C1D3600CDC6183A0C1C32081D0E45CDD5181FD26203CDBD1521651B3A641BBECA5C033A0400E6F0F53A651BE60FC148B1320400CD070DC3EE0A0E48CDD5181FD28603CDBD153A651B32211D3A641B32651B3A211D32641BC3BB02C3EE0A3A211DFE49C2AC05218A1B3A0C1C96D6019FF53A071DD600D6019FC148A1320C1D1FD2AE03CD58113A681B32691BCD03161FD290053A211DFE00CA8D053A211DD615D6019FF53A211DD618D6019FC148B1F53A211DD612D6019FC148B11FD22C04210000221C1D21201D3600237EFE12C20204CDEF0BCD4617C32904CDB712CD58143A211DFE15C21904CDEF0BCD5811C329043A691B21681BBED22904CD390BC31904C365053A211DFE08C2CA043A681B326A1B4F3E00B9D246040E20CD4C103E0121221DCD051A9FF53A691B216A1B969FC148A11FD2BC042A221D2B014D1D097EFE0ACABC04CDB21321661B360121681B3600210000221C1D21201D3600CD461721661B36003A681B326B1B21691BBED2A0043A691B326B1B3A6A1B32681B3A6B1B21681BBED2B604CD390BC3A6043A681B326A1B21211D36003A6A1B32681BC365053A211DFE7FC20A053E0111221DCDFA19B5C2E104C3F502CDB2132A221D014D1D097E32211D4FCDCB0B3A211DFE0AC202052A101C2B22101C21211D3600C365052A211D4DCD9B0B2F1FD224050E5ECD4C103A211DC6404FCD4C103A211DFE0CC23205CD9118C365053A071DFE00C262053A211DFE20DA4C053A681B3C32681BC362053A211DFE09C262053A681BE6074F3E089121681B8677CDD9153A211DFE0AC27005CD58113A211DFE0DC2850521211D360A0E0ACD4C10C38A0521211D3600C3BB03C3B4033A211DFE1ACA9B05CD91183A141C210C1DA61FD2A905CDEF0BC3EE0A0E4FCDFC181FD2B805C3BB023A211DFE52C2860621941A3601CD4712CD03161FD2E1053E0821941ABED2DB05C3D702CD1D17C3C80521211D36203A941AD601D6019F32061D1FD20E062E0811741A01961A0A1203132DC2FD0501951ACD3F0CC31D063E0821941ABEDA1D06CD1D17C30E06217F1A360021931A360001731ACD2C0C21941A36803A781BFEFFC2420621061D364FC3F502CD551232211DFE1ACA5306CDD915C3420621471D36003A061D1FD2830621361B3A471DBED283062A471D260001B61A097E32211D3A471D3C32471DCDD915C35F06C3EE0A0E51CDFC181FD29B06013D1BCD650CCD070DC3EE0ACD62183A211DFE2DC2AE06CDC61821201D36003A211DFE23C2BF06CD8712CDC618C3FC06CD3B191FD2DC06CD4A193A211DFE3AC2D90621211D364CCD7619C3FC063A211DFE3AC2FC06CDC618CD4A19CD76193A201DFE01C2FC062A1C1D23221C1DCD8E121FD2080721201D36003A211DFE42C2290721201D3E01967721010022261D2A6F1A22281DCD5214C3EE0A3A211DFE43C23A07CD6514CD5214C3EE0A3A211DFE44C24B07CD6514CD5814C3EE0A3A211DFE4BC25C07CDB712CD5814C3EE0A3A211DFE4CC26A07CD5E14C3EE0A3A211DFE50C29A07CD8E121FD2870721201D3601CDDA17CD4617C39707CDA2121FD29707CDEF17CD3A18C38707C3EE0A3A211DFE54C2A807CD4617C3EE0A3A211DFE55C2BE073A201DD601D6019F32101DC3EE0A3A211DFE56C2FC07CD8E121FD2EE0701221D11241DCDED19444DCDA9100E2FCDCB0B2A6F1A444DCDA910CDEF0BC3F9073A201DD601D6019F32141CC3EE0A3A211DFE0DC228083A081DD601D6019FF53A071DD600D6019FC148A11FD22508CD5E14CD6218CD4617C3EE0A3A201DD601D6019FF5CD8E12C148B11FD2EB0A3A211DFE41C2790821201D36012A221D22261D2A6F1A22281DCD5214CD8E121FD26108CD6E18CDA2121FD26E08CDC414C3610821201D3600CD5214C3E80A3A211DFE46C29408CDFC16CDA2121FD29108CD0B17C38408C3E80A3A211DFE4AC21A09CDFC16CD24163A041D32051DCD2416CDA2121FD21709CD0B173A031D3D32081D3A081D3C32081D21051DBED2DD082A081D2600019F1C097E32211DCDD915C3BC082A221D22491D2A051D4D2A041DEBCD6F162F1FD2F608C3D70221051D3A041D9611221DCDFA1922261D21201D3600CD52142A491D22221DC3AB08C3E80A3A211DD64DD6019FF53A071DD600D6019FC148A11FD27C0921091D36FF3E01111C1DCDFA19B5C24609CD9B12CD6511F53A091D3C32091D4F0600211F1C09C1487179FE0DCA6409C346093A091D32071D21091D3600E52A1C1DEBE123732372C3E80A3A211DFE4EC2DD09CDFC16CDA2121FD2DA092A031DEB0E00CD6F162F1FD2D709CDC40C1FD2A609C3F502CDE117CDB615CD6E1821201D360021010022261DCD5214CDE81721201D3601016F1A11241DCDED19DAD409C3D702C38E09C38709C3E80A3A211DFE53C2300ACDFC16CD2416CDA2121FD22D0ACD0B173A031D32081D11221DCDFA19EB2B73237221041D3A081DBED22A0A2A081D2600019F1C097E32211D3A081D3C32081DCDD915C3060AC3EB09C3E80A3A211DFE57C23E0ACD7615C3E80A3A211DFE58C2B30ACD5A0ECD8E121FD25C0ACD450E01951ACD650CC3B00A3A371B2F1FD2830ACD450E21371B360101951ACD650C01951ACD950C3A781BFEFFC2830ACD2D0DCDB7122A261D224B1D11281D014B1DCDED19DAB00A2A4B1D014D1D094ECD940F1101002A4B1D19224B1DD28C0AC3E80A3A211DFE5AC2DD0ACD8E121FD2CD0ACDF30AFE1AC2CD0AC3F502CDA2121FD2DA0ACD3A18C3CD0AC3E80A3A211DFE00CAE80AC3E202C3EE0AC3E202C32703FB761100000E01CD0500C921671B713A661B1FD2080BC92A671B2600EB0E02CD0500C9216C1B713A6C1BFE20DA240B21681B343A6C1BFE0AC2310B21681B36002A6C1B4DCDFC0AC93A681BFE00C2420BC90E08CD140B0E20CD140B0E08CD140B21681B3535C9216D1B713A6D1BD609D6019FF53A681BE6074F3E0791C148A1326E1B791FD27D0B216D1B3620216F1B36003A6E1B216F1BBEDA9A0B2A6D1B4DCD140B216F1B34C2820BC921701B713A701BFE20DAAA0B3E01C93A701BD60DD6019FF53A701BD60AD6019FC148B1F53A701BD609D6019FC148B1C921711B712A711B4DCD9B0B1FDAE70B0E5ECD570B3A711BC64032711B2A711B4DCD570BC90E0DCDCB0B0E0ACDCB0BC921731B702B712A721BEB0E09CD0500C921751B702B71CDEF0B2A741B444DCDFA0BC921771B702B712A761BEB0E0ACD0500C9217A1B702B712A791BEB0E0FCD050032781BC9217C1B702B712A7B1BEB0E10CD050032781BC9217E1B702B712A7D1BEB0E11CD050032781BC921801B702B712A7F1BEB0E13CD0500C921821B702B712A811BEB0E14CD0500C921841B702B712A831BEB0E15CD0500C921861B702B712A851BEB0E16CD050032781BC921881B702B712A871BEB0E17CD0500C921891B368001891BCD1C0CC91100000E0BCD05001FD2DB0C1100000E01CD05003E01C93E00C91100000E19CD0500C9210D1C712A0D1C2600EB0E0ECD0500C9210F1C702B712A0E1CEB0E1ACD0500C93A371B1FD2140D01951ACD650CCD0000C921161C702B712A151C444DCD0A0CCDEF0BCD070DC9013D1BCD3F0C012901CD180DC921181C702B712E03E52A171C444D11461BE10A1203132DC24C0DC92A391B22601B21000022621B2168003600216A003600217C0036002E21113D1B015C000A1203132DC2780D21651B3A641BBECAA50D2A651B4DCDE70C015C00CD520C3A781BFEFFCAA50D014001CD180D2A641B4DCDE70C015C00CD2C0C3A781BFEFFC2D70D015C00CD950C3A781BFEFFC2CB0DCD2D0D015601CD0A0CCDEF0BC3F90D3A6500071FD2EB0D015F01CD0A0CCDEF0BC3F90D3A6600071FD2F90D017701CD180D019401CD3A0D013D1BCD650C21651B3A641BBECA1C0E2A651B4DCDE70C013D1BCD650C019701CD3A0D013D1BCD650C013D1BCD950C215D1B36003A781BFEFFC23E0ECD2D0D21010022101CC921371B360021A11A360021B51A360021361B3600C92A641B4DCDE70C01B61ACDF70CC9CDD30E2A641B4DCDE70C21191C36003A381B21191CBEDACF0E2A601BEB2A3B1B19444DCDF70C015C00CD750C32781BFE00CABB0E3E0121781BBED2A80ECD2D0D2A601BEB2A3B1B19361A3A381B32191CC3C50E1180002A601B1922601B3A191C3C32191CC2770ECDD30EC921000022601BC901391B11601BCDED19DAE90ECD680E2A601BEB2A3B1B197E321A1CFE1ACA010F2A601B2322601B3A1A1CC92A651B4DCDE70C0E0721621BCDD3192BEB211C1C737BFEFFC2210FC9CD660F211B1C36003A1C1C211B1CBEDA620F2A621BEB2A5E1B19444DCDF70C013D1BCD850CFE00CA4E0FCD2D0D1180002A621B1922621B3A1B1C3C321B1CC2290FCD660FC921000022621BC9211D1C7101391B11621BCDED19DA800FCD050F2A621BEB2A5E1B193A1D1C772A621B2322621BC9211E1C713A361BFE80DAB60FCD5A0E01951ACD850CFE00CAB10FCD2D0D21361B36002A361B260001B61A093A1E1C773A361B3C32361BC9214A1B36002A621B7DE67FFE00CAEA0F3A4A1B3C324A1B0E1ACD6D0FC3D00FCD050F013D1BCD3F0C3A781BFEFFC2FE0FCD2D0D019A01CD3A0DCD3B102A641B4DCDE70C2E10113D1B015C000A1203132DC21610013D1BCDA80CCD3B10019D01CD3A0D2A651B4DCDE70C013D1BCDA80CC92E10114D1B013D1B0A1203132DC24310C9210E1D713A071DFE00CA5910C92A0E1D4DCDCB0BC921111D713A111DD6619F2FF53E7A21111D969F2FC148A1C921121D712A121D4DCD61101FD28E103A121DE65FC93A121DC921131D713A0F1D1FD2A5102A131D4DCD7910C93A131DC921151D702B7121102722181D21171D36003E0011181DCDFA19B5CA18112A141DEB2A181DCDAA197B32161D2A141DEB2A181DCDAA1922141D2A181DEB210A00CDAA19EB22181D3A161DD600C6FF9F21171DB61FD2101121171D36013A161DC6304FCDCB0BC315110E20CDCB0BC3BA10C9211B1D702B713A141C2F1FD22811C92A1A1D444DCDA9100E3ACDCB0B0E20CDCB0B3A0C1D1FD249110E20CDCB0BC34E110E2ACDCB0BC92A101C444DCD1911C93A071DFE00CA6111C9CD4F11C93E0021071DBED2BA11CDC40C1FD27811C3D70221071D3A091DBEDAA6113E00110A1DCDFA19B5CAA1112A0A1D2B220A1D3E00CDE319B5C2A111C3D70221091D36003A091D3C32091D3D4F0600211F1C094ECD9210C93A0C1D1FD2C911CDF30A4FCD9210C93A0D1D1FD21712210D1D36003A681BD600D6019F21141CA61FD20012016F1A11241DCDED19DAFA11010000CD1911C3FD11CD4F11C305120E2ACDCB0BCDB80C210C1C36000E0ACDCB0B21681B3600218A1B3A0C1C96D6019F320D1D1FD233122A0C1C2600018B1B09360D3A0C1C3C320C1C3D4F0600218B1B094ECD9210C92A641B4DCDE70C018000CDF70CC93A941AFE80DA7312CD471201731ACD750CFE00CA6E123E1AC921941A36003A941A3C32941A3D4F0600218000094ECD9210C921FFFF221C1DC93E00111C1DCDFA19B5D6019FC9210000221C1DC9CD8E122F1FD2B4122A1C1D2B221C1D3E01C93E00C921010022121C3A201DFE00C2DF122A1C1D23221C1D2A221D222A1D210000222E1D2B222C1DC3F1122A241D222A1D2A6F1A222E1D210100222C1D21331D36013A331D1FD27B13012E1D112A1DCDED19B5C6FF9F32321D2A2C1DEB2A2A1D1922301D014D1D09F57ED60AC6FF9FC148A11FD233132A301D222A1DC3FD122A121C2B22121C2A1C1D2B221C1D3E00CDE319B5C6FF9F32331D3A321D2F1FD26B1321331D3600012C1D112A1DCDED19EB2B732372C378133A331D1FD278132A301D222A1DC3F6123A201DFE00C293132A2A1D22261D2A221D2B22281DC3A1132A241D2322261D2A2A1D2322281DC92A221D2322221DC92A241D2322241DC92A221D2B22221DC92A241D2B22241DC92A101C2322101CC921341D71114D1D2A221D19E52A241D19E53A201DFE01C212142A281D791FDAF11322241DE1E1C919444DE1D17D917C98D24314237EFE0AC20D14E52A101C2322101CE11213C3F6132A261D19444DE1D17993789AD243141B1AFE0AC23114E52A101C2B22101CE1F53A341D1FD23F14F1772BC31A14F1C31A14D511B3E21922241DE11922221DC9C90E01CDCA13C90E00CDCA13C9CDB712CD5214C93A201DFE00C297142A241D22281D11221D011C1DCDED19D2881421010022261DC39414011C1D11221DCDED1922261DC3C3142A221D22261D01241D116D1ACDED19111C1DCDFD19DAB8142A6F1A22281DC3C3142A1C1DEB2A241D1922281DC901241D11221DCDED19DAD314C3ED02CDDA0E4FCD041532361DFE1AC2E614CD9B12C92A221D014D1D093A361D77CDA2133A361DFE0AC20015CDC213C9C3C414C921371D713A101D1FD217152A371D4DCD9210C93A371DC9016F1A11241DCDED19DA2B15CD9B12C9CDAA132A241D014D1D097E32381D4FCD6D0F3A381DFE0AC24915CDC213C9C31B15C9CD8712CDA2121FD2751501241D116F1ACDED1911711ACDFD19DA6F15CD9B12C37215CD1B15C35015C921201D360021010022261D2A241D22281DCD5214CD8E121FD29415CD4D15CDA2121FD2A115CD1B15C3941501281D11241DCDED19D2B51521201D3601CD5214C9CD8712CD7615C9CDB615CDDA0E32211DFE1ACAD5152A211D4DCD6D0FC3C015CDCB0FC901241D11221DCDED19B5C2E915C3ED022A221D014D1D093A211D77CDA2133A211DFE0AC20216CDC213C9CD651132211DD61AD6019FF53A211DD60DD6019FF53A0C1D2FC148A1C148B12FC9CD03161FD251163A211DFE0CC2401621211D360DCD521621211D360A3A211DFE00C24B16C3E202CD5216C32416C92A041D2600019F1C093A211D773A041D3C32041DFE64DA6E16C3ED02C9213A1D732B712A241D223B1D213E1D36003A3E1D2F113B1D016F1AF5CDED199FC148A11FD2E7162A3B1D2322281D223B1D3A391D323D1D2A3D1D2600019F1C09E52A281D014D1D09D11A96D6019F213A1DF53A3D1D96D6019F323E1D2FC148A11FD2E4163A3D1D3C323D1D2A281D2322281DC3A616C380163A3E1D1FD2F8162A281D2B22281DCD52143A3E1DC921041D3600CD24163A041D32031DC92A031DEB0E00CD6F162F1FD21C17C3D702C92A211D4DCD7910F53A941A3C32941A3D4F060021731A09C14871C92A121CEB2A101C19444DCD1911C9CDB712210C1D36013A201DFE01C2651721000022121C2A221D223F1DC36B172A261D223F1D2A3F1D2B014D1D097E32411DD60AD6019FF53A681BD600C6FF9FC148A11FD28F17CDEF0B2A261D223F1D11281D013F1DCDED19DAD9173A411DFE0AC2BD17CD38172A121C2322121CCDC40C1FD2BD17C3D7022A3F1D014D1D097E32411D4FCDCB0B1101002A3F1D19223F1DD29517C9211700221C1DC92A1C1D221E1DC92A1E1D221C1DC9CDE117CDDA17CD5E143A201D32421D21201D3601CDDA17CD46173A421D32201D016F1A11281DCDED19B5D6019FF53E0111261DCDFA19B5D6019FC148B11FD23618CD9B12C33918CDE817C921431D36003E1321431DBEDA6118CDC40C1FD25218C3F5023EFACD111A3A431D3C32431DC23F18C921201D3601210100221C1DC9CD8712CDA2121FD2901801711A11221DCDED19DA8A18CD9B12C38D18CDC414C37118C921211D360DCDD91521211D360ACDD915C9210F1D36012A211D4DCD611032441D2A211D4DCD921032211D3A441D2F21101DB62B77C9210F1D3600CD651132211DCDA218C921451D7121451D3A211D96D6019FF53A8A1BD601D6019FC148A1F53A071DD600D6019FC148A1C921461D712A461D4DCDD5181FD23819CDEF0B2A461D4DCDFC0A11A0010E09CD0500CDF30A4FCD791032461DCDEF0B3A461DFE59CA3519C31A033E01C93E00C93A211DD63032481D4F3E09919F2FC9210000221C1DCD3B191FD275192A1C1D292929E52A1C1D29C109E52A481D2600C109221C1DCDC618C35019C911101C011C1DCDED19D2981921201D360101101C111C1DCDED19EB2B732372C3A91921201D3600011C1D11101CCDED19221C1DC9444D2100003E10F529EB9729EB8D916F7C986713D2C319091BF13DC2B119C95E2356EB290DC2CD19C95E2356EB7CB71F677D1F6F0DC2D719C95F16007B956F7A9C67C969604E23461A916F131A9867C96F26001A956F131A9C67C95F16007B965F7A239E57EBC9060C480DC2141A3DC2131AC900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020202020202020204C4942000000000000000000000000000000000000000000000058242424242424244C49420000001A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A>0073 +A:DOWNLOAD ASM.COM +U0 +:3100022A060022CD01C3000220434F5059524947485428432920313937382C204449474954414C2052455345415243482000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F02000000000000000F020000000000000000000000000000000000000000000000000000000000000000000000000000000000000C3E00CC3A10DC3CA0DC3340EC3AA0EC3DE0EC3BC0CC3000FC32F0FC34C10C3390F000000000000000000000000000000000000000000000000000000000000000041534D00000000000000000000000000000000000000000000000000000000000050524E00000000000000000000000000000000000000000000000000000000000048455800000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000213402BEC8775F0E0ECD0500C9237EFE20CAB80CDE41C93A3402C97ECDDE0E7E23FE0DC2BC0C3E0ACDDE0EC9115C0006091AFE3FCABB0D77231305C2D20CC921A00FCDBC0CC33F0D0E0FCD0500FEFFC021B90FCDBC0CC300000E10CD0500FEFFC0212910CDBC0CC300000E13C305000E16CD0500FEFFC021D00FCDBC0CC300003A3502CDA10CC93A3602FE19C8FE17C93A3602CDA10CC93A3702CDA10CC93A5C00FE20CABB0D0E19CD0500323402216400CDAE0C323502CDAE0C323702CDAE0C323602213802CDCD0CCD280DCA830D215902E5E5CDCD0CCD310DD1CD0B0DD1CD100D3A3702FE19CA9E0D217A02E5E5CDCD0CCD380DD1CD0B0DD1CD100DC30011210004229B02AF324402325802322302CD210D113802CDE90CC921E30FCDBC0CC300007ABCC07BBDC9C5D5E52A9B02110004CDC40DC2190ECD210D210000229B020608219D02C5E50E14113802CD0500E1C1B70E80C20D0E1180000E801A7713230DC2FE0D05C2E70DC3190EFE03D22B0E361A230DC2120E119D022A9B02E523229B02E1197EE1D1C1C921FA0FCDBC0CC30000C5473A3602FE19CA510EFE1778C24A0ECDDE0EC3510ED5E5CD530EE1D1C1C92A9D06EB219F061977EB23229D06EB210003CDC40DC0CD310D210000229D06219F0611590206067EFE1AC8C5D50E801180007E1223130DC2850ED1D5E50E15CD0500E1D1C1B7C2A10E05C8C37A0E211110CDBC0CC3770FC5D5E5CDB40EE1D1C1C92A9F09EB21A1091977EB23229F09EB210003CDC40DC0CD380D210000229F0921A109117A020606C37A0EC5D5E50E025FCD0500E1D1C1C94FCD340E3A0C01FE20C83A3602FE17C879CDDE0EC93A8401210C01B7CA150F477ECDEB0E23783DC3060F3284013E0DCDEB0E3E0ACDEB0E210C013E783620233DC2270FC947210C017EFE20C070C9CD280DCA4F0F2A9D067DB4CA4F0F3E1ACD340EC33F0F3A3702FE19CA770F3A2302B7C4B8102AD001222102CDB8102A9F097DB4CA770F3E1ACDAA0EC3670FCD280DCA860FCD310D115902CDFA0C3A3702FE19CA970FCD380D117A02CDFA0C213C10CDBC0CC3000043502F4D20415353454D424C4552202D2056455220322E300D4E4F20534F555243452046494C452050524553454E540D4E4F204449524543544F52592053504143450D534F555243452046494C45204E414D45204552524F520D534F555243452046494C452052454144204552524F520D4F55545055542046494C45205752495445204552524F520D43414E4E4F5420434C4F53452046494C45530D454E44204F4620415353454D424C590DC5473A3702FE1978CA9810D5F52123027EB7CA8410FE10DA6C10CDB810C384102AD001EB2A21024F0600097BBDC281107ABCCA8A10CDB8102AD0012221022123025E34160021240219F177D1C1C9F50F0F0F0FE60FCDAF10F1F5E60FCDAF10F18257C9C69027CE4027C3AA0E3E3ACDAA0E2123025EAF57772A21027BCD9A107CCD9A107DCD9A10AFCD9A107BB7CAE8102124027E23CD9A101DC2DF10AF92CD9A103E0DCDAA0E3E0ACDAA0EC90000000000000000C34013C33211C3C011000000CD0602F5FE0DCA3011FE0ACA30113A8401FE78D230115F16003C328401210C0119F177C9F1C9CD4911320A113284013E0A320911CD15023E10328401C9AF328801320B11C92188017EFE40DA5F113600CD1E135E16003423193A0A1177C97EFE24C0AF77C93A0A11D630FE0A17E601C9CD7111C03A0A11D641FE0617E601C93A0A11D641FE1A17E601C9CD8B11C0CD7111C93A0A11FE61D8FE7BD0E65F320A11C9CD0C11320A11C32D13C9FE0DC8FE1AC8FE21C9AF328501CD49113A0A11FE09CAF411FE3BCAE111FE2AC2ED113A0911FE0AC2ED11CDAD11CDB711CAFA11C3E111F620FE20C2FA11CDAD11C3C711CD8B11CA05123E01C33912CD7111CA10123E02C339123A0A11FE27C22112AF320A113E03C33912FE0AC237123ACF01B7C41502210C0136203E103284013E043285013A0A11320911B7C45111CDAD113A8501FE04C8FE03C49E11210A113A8501FE01C26C12CD6A11CA3C12CD9611C8C33C12FE02C20213CD6A11CA3C12CD7C11C23C123A0A11FE4FCA8A12FE51C28F123E08C39612FE48C2A0123E10320B11AF320A11C3BB123A0911FE42C2AD123E02C3B412FE443E0AC2B81221880135320B112100002286012188014E237E23FE41D2D212D630C3D412D637E5C54F210B11BED4181306007E2A8601EB210000B7CAF7121FD2F11219EB29EBC3E81209228601C1E10DC2C612C93A0A11FE0DCA1E13FE27C23C12CDAD11FE27C0C33C12F53E56C32413F53E4FC32413C5E5CD1802E1C1F1C9F53A8501FE03C49E11F1C90000000000000000C3A015C35C14C39E14C39814C3EB14C36015C37215C38D15C396150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000215B130680AF7723772305C2621421000022D601C921880146AF238605C27614E67F325B14C9472AD60123237EE6F0B077C92AD60123237EE60F3CC92AD6017DB4C9CD71142188017EFE11DAAC143610215B145E1600215B1319195E23666B22D601CD9814C8CD8E14218801BEC2E1144723EB2AD6012323231ABEC2E114132305C2D514C92AD6015E2356EBC3BB142188015E16002ACB0122D6011911050019EB2ACD017B957A9CEBD2411522CB012AD601EB215B144E0600215B1309094E2346722B73EB7123701188011AFE11DA2F153E10473D237723131A7705C23315AF23772377C9214A15CD1202C31E0253594D424F4C205441424C45204F564552464C4F570D17171717E6F0472AD60123237EE60FB077C92AD60123237E1F1F1F1FE60FC9CD8E142AD6015F160019232323C9E5CD7F15D1732372C9CD7F155E2356EBC90000C36018C38317C31018C415D415E6158216AE16BD161009340B03BD16DD16EF1657176D170D28292A2B2C2D2F4142434445484C4D444244494453445745494946494E4F525350414349414443414444414449414E41414E44414E49434D41434D43434D50435049444141444144444352444358454E44455155484C54494E52494E584A4D504C44414C58494D4F444D4F564D56494E4F504E4F544F52414F52474F52494F5554504F5050535752414C524152524554524C4352524352535453424253424953455453484C534852535441535443535542535549584F5258524158524943414C4C454E444D4C4441584C484C445043484C5055534853484C445350484C53544158584348475854484C454E4449464D4143524F5449544C450F0A0C140D1E005005460E0A0646015010071000100110021003100410051006110113F31102110313FB110821DB0A2810061ACE1D881D801AC61DA009321AE6132F133F1DB81AFE132715091E051F0B1104110713761E041F0317C31C3A14010250184019061300083C1DB0110A1AF621D316C110061317131F13C91307130F20C71D981ADE110B035004501C3213371D901AD60B281DA81AEE17CD11061B0A1C2A13E916C51C2213F91B0213EB13E311051109110C4E5A5A204E434320504F504550204D201EFF040E00AF78811FBBCAC4175FE5D5C5E5424816002100001905C29C17D1191189011ABE1323C2B6170DC2A617C1D1E17BC9C1D1E1DAC0174BC3881743C38817AF3CC93A89010117C2FE4AC806C4FE43C80113C0FE52C93A8801FE04D20D18FE03CAF217FE02C20D18218B013620010800117317218A011ABE13C205181A23BEC813040DC2F8170CC9AF3CC93A88014F3D5F1600D5FE05D25A1821B515194621A91519195623666A51CD8317C24518D121BA1519195E23566F260029197E2346C9D1CDC717C0C5CDDB1778C1C0B7171717B04779BFC9D1AF3CC90000C3A01BC3191AC36E19C33819000000000000000000000000000000000000000000000000000000000000000000000000000000EB2192187EFE10DAA218CD851B36007E34344F060021811809732372C9F52191187EFE0ADABF183600CD851B5E160034F1216D1819772177181970C92192187EB7C2DE18CD851B210000C935354E0600218118094E236669C9CDCF18EBCDCF18C96F260029110119195E23666BE98919921999199F19AB19BF19C619D019D919E019EC19F819851BCDEC187AB7C227197BFE11D8CD851B3E10C9AF956F3E009C67C9CDEC18EB226B19216D193611010000C5AF7B175F7A175735E1C83E00CE002944852A6B19954F789C47C5D2641909E3216D193FC34619000000444D210000AF781F47791F4FDA8219B0C8C3831919EB29EBC37319CDEC18CD6E19C3011ACD3519EBC3011ACD3519C3011ACD1B19B7CA011A293DC3A219CD1B19B7CA011AF5AF7C1F677D1F6FF13DC3AE19CDEC1819C3011ACDEC18EBCD2D19C3C219CDCF18CD2D19C3011ACDCF1823C3D319CDEC187AA4677BA56FC3011ACDEC187AB4677BB56FC3011ACDEC187AAC677BAD6FC393183A8501FE04C03A8901FE0DC8FE3BC8FE2CC8FE21C9AF3291183292183D326C1821000022C901CD041AC25D1A2191187EB7CA481A355F1D1600216D18197ECDF418C3301A3A9218FE02C4851B3A0C01FE20C02A811822C901C93A0C01FE20C27F1B3A8501FE03C2891A3A8801B7CC851BFE03D4851B16002189015E233DCA851A56EBC3711BFE02C2941A2A8601C3711BCDA615C2311BFE10D2261BFE0C4F3A6C18C2B51AB7CC851B3EFF326C1879C3031BB7C20E1BC53A9118B7CADE1A5F1D1600217718197EB8DADE1A21911873216D18197ECDF418C1C3B91AC179FE0DC2031B2191187EB7CAFC1A3D775F1600216D18197EFE0CCAFF1ACD851BAFC3081BCDB0183EFF326C18C37F1B79FE05CA7F1BFE06C21E1B3C4FC3B91AFE08C4851BC3B91AFE11CC851B682600C3711B3A8501FE04C2501B3A8901FE24CA4A1BCD851B210000C3711B2AD201C3711BCD4613CD4913C2641B3E50CD1802CD4C13C36E1BCD5213E6073E55CC1802CD58133A6C18B7CC851BAF326C18CD9318CD0611C32A1AE53E45CD1802E1C900000000000000000000000000000000000000AF32CF01CD4313CD0311CD030221000022EB2022D00122D20122ED20CD06113A8501FE02CABC1BFE04C2DD1B3A8901FE2AC2311FCD0020C27C1FC3521FFE01C27C1FCDA615CA301CCD4613CD4913C2FE1BCD4C133ACF01B7C4D720C30C1CCD5213FE06C20C1CCDE320C3521F2AEB207DB4C4DD202AD60122EB20CD06113A8501FE04C2BF1B3A8901FE3AC2BF1BC3BC1BFE11C2D71D5816001B21431C19195E23666BE95B1CA91CC01CDE1C151D181D1E1D401D871D8D1DA71DCE1DCD0A20CD06113A8501FE03C28C1C3A88013DCA8C1C47040421890105CA861CC54623E5CD4820E1C1C3761CCD0611C39B1CCD63182AC9017CB7C4D12045CD4820CDF91FCDBA1EFE2CCA5E1CC3311FCD0A20CDA620CDD11EEB2AD2011922D20122D001C3311FCD0A20CDD11EE545CD4820E144CD4820CDF91FCDBA1EFE2CCAC31CC3311FCD0A20CDA6203A0C01FE20C2311FCDD11E3A0C01FE20C2FA1C22ED203E20320C01CD06113A8501FE04C27C1F3A8901FE0AC27C1FC38B1FC3D11DCDE320C3D11DCD0020CA7C1F2AD201E5CDD11E22D201CD0A20CDA920211201363DE122D201C3311FCD0A20CDD11E3A0C01FE20C2311F7D1FDA311FCD06113A8501FE04C26E1D3A8901FE1A3E42CC1802CA8B1FC3531DFE01C2531DCDA615C2531DFE11C2531D78FE05C2531DC3D11DCDE320C3311FCDD11E3A0C01FE20C2311F22D20122D001CD0A20CDA620C3311FCD0020CA7C1FCD5213FE05C4DD203E05CD4F13CDD11EE5CD0020E1CD551321000022EB20C3311FCDE320CD0611C3311FD613FE21D27C1F5F160021EB1D19195E23666BE9091E121E1E1E241E381E411E501E601E691E781E811E881E8F1E9E1EA51ECD4820CD0611C3B11ECDFC1ECD171FCD111FC3B11ECDFC1EC3B11ECDF21EFE38CA311EE608C4BD2079E630B0C3AE1ECD4820CD111FC3B11ECDF21EB047CD171FCDE71EB0C3AE1ECDF21EB0CD4720CD171FCD0B1FC3B11ECD4820CD0B1FC3B11ECDF21EE628C4BD2079E610B0C3AE1ECD4820CD111FC3B11ECDE71EB0C3AE1ECDF21EB0C3AE1ECDF21EE608C4BD2079E630B0C3AE1ECDF21EB0C3AE1ECD4820CD0B1FC3B11ECD4720CD0A20CDF91FC3311F3A8501FE04C4D1203A8901FE2CC8FE3BC8FE0DC4D120C9C5CD0611CD63182AC901C1C9CDD11E7CB7C4C7207DC9CDDD1EFE08D4C720E607C9CDE71E171717E6384FC9CDF21EE608C4BD2079E630B0C34720CDDD1EC34720CDD11EC37420F5C53A8501FE04C2291F3A8901FE2CCA2E1F3E43CD1802C1F1C9CD0A203A8501FE04C27C1F3A8901FE0DC24A1FCD0611C3BC1BFE3BC2721FCD0A20CD06113A8501FE04C2521F3A8901FE0ACABC1BFE1ACA8B1FFE21CABC1BC3521FFE21CABC1BFE1ACA8B1F3E53CD1802C3521F7B956F7A9C67C921CF017E34B7CAA71BCD0611CDA620211101360D210D01CD12022ACB01EB2AD401CD841FE52ACD01EB2AD401CD841F5C1600E1CD6918EBCDA92021110111D61F1AB7CAE41F772313C3CB1F482055534520464143544F520D00210E01CD12022AED2022D001C31E027ABCC07BBDC92AD00122D201C92AEB2022D601CD4913C9CD0020C821000022EB203ACF01B7C23120CD5213F5E607C4DD20F1F601CD4F132AD201CD5513C9CD5213E607CCD720CD5813EB2AD201CDF31FC4D720C9473ACF01B778CA6C20C5CD1B023A0D01FE202AD201CCA9203AEF20FE10C1D26C2078CD96202AD0012322D001C9E545CD4820E144C34820C630FE3AD8C607C9CD7E2021EF205E160034210C011977C9F51F1F1F1FE60FCD8620F1E60FC386202AD201EB21EF20E536017AD5CD9620D17BCD9620E134C9F5C53E52CD1802C1F1C9F5E53E56CD1802E1F1C9F53E44C3E620F53E50C3E620F53E4CC3E620F53E4ECD1802F1C9000000000000000000000000000000000000000000>0059 diff --git a/Z80 CPM and bootloader (basmon)/windowsApp/COMDLG32.OCX b/Z80 CPM and bootloader (basmon)/windowsApp/COMDLG32.OCX new file mode 100644 index 0000000..84022fb Binary files /dev/null and b/Z80 CPM and bootloader (basmon)/windowsApp/COMDLG32.OCX differ diff --git a/Z80 CPM and bootloader (basmon)/windowsApp/FilePackage.exe b/Z80 CPM and bootloader (basmon)/windowsApp/FilePackage.exe new file mode 100644 index 0000000..2642a7a Binary files /dev/null and b/Z80 CPM and bootloader (basmon)/windowsApp/FilePackage.exe differ diff --git a/build_id.v b/build_id.v index eb65496..2481b81 100644 --- a/build_id.v +++ b/build_id.v @@ -1 +1 @@ -`define BUILD_DATE "201121" \ No newline at end of file +`define BUILD_DATE "201221" \ No newline at end of file