.Z80 ORG 0100H START: JP MAIN ; 1. Read console - returns an ascii character ; 2. Write console - Sends an ascii character ; 3. Read reader ; 4. Write punch ; 5. Write list ; 6. Direct console input/output. Send FF to receive character status ; 7. Get I/O status of device ; 8. Set I/O status ; 9. Print buffer. Send entire string starting with address and ending With $ ; 10. Read buffer. Send address of buffer and return with filled buffer ; 11. Interrogate console ready (If LSB is 1 then console character is ready) ; 12. Lift disk head and return version number of CP/M ; 13. Reset disk system. ; 14. Select disk. 1=A, 2=B etc ; 15. Open file. ; 16. Close file. ; 17. Search for a file. ; 18. Search for next occurrence. ; 19. Delete file ; 20. Read sequentially the next 128 bytes. ; 21. Write sequentially the next 128 bytes. ; 22. Make new file and open it. ; 23. Rename file. ; 24. Return login vector (which disks are online). ; 25. Return current disk. ; 26. Set DMA address (to find data records in other parts of memory.Usually boot+80H) ; 27. Get allocation vector (for STAT to calculate remaining space) ; 28. Write protect disk. ; 29. Get read/write status of disk. ; 30. Set file attributes (read only etc) ; 31. Get disk parameter block address. ; 32. Get or set user code. ; 33. Read randomly. ; 34. Write randomly. ; 35. Compute file size for random files. ; 36. Set random record position. FDOS EQU 0005H ; COMMON ENTRY POINT FOR BIOS AND BDOS UART EQU 068H ; UART LOCATION TEST_TEXT: DB 'HELLO WORLD$' TEST_STRING: DB 'SAVING TEST.TXT$' PROMPT_STRING: DB 'ENTER A CHARACTER$' UCOPYATOU DB 'COPY FILE FROM DRIVE A TO UDRIVE$' CRLF: DB 13,10,'$' READBUFF: DB 'ABCDEFGHIJKLMNOPQRSTUVWXYZ$' FCB: DB 0,'FILENAMECOM',0,0,0,0,0 DB ' ',0,0,0,0,0,0,0,0 UDRIVE_STRING: DB 'UDRIVE INITIALISED AT CURRENT BAUD RATE$' ERRORSTRING: DB 13,10,'ERROR$' DIRECTORYLIST: DB 'DIRECTORY LISTING OF DRIVE U:$' FINISH: DB 'FINISHED$' ASCIIBIN: DB '12345$' BINASCII: DB '12345$' FILENAME: DB '12345678$' ; 1 to 8 characters long, no padding, space for 8 FILEEXT: DB 'COM$' ; always 3 characters long COMMAND_STRING: DB '12345678901234567890123456789012$' ; 32 BYTES PER STRING, END WITH $ STRING1: DB '1234567890ABCDabcd90123456789012$' ; 32 BYTES PER STRING, END WITH $ STRING2: DB '12345678901234567890123456789012$' ; 32 BYTES PER STRING, END WITH $ STRING3: DB '12345678901234567890123456789012$' ; 32 BYTES PER STRING, END WITH $ STRING4: DB '12345678901234567890123456789012$' ; 32 BYTES PER STRING, END WITH $ STRING5: DB '12345678901234567890123456789012$' ; 32 BYTES PER STRING, END WITH $ STRING6: DB '12345678901234567890123456789012$' ; 32 BYTES PER STRING, END WITH $ STRING7: DB 'NAME.TXT$' ; ************************************************ MAIN: LD DE,STRING1 CALL STRINGS_UPPER CALL PRINT RET ; back to cp/m ; ************************************************ PRINT: ; print as in BASIC print. Could call this PRINTF for like in C. Adds CR CALL WRITE_STRING_CR RET ; ************************************************ ; bdos calls that preserve registers WARM_BOOT: LD C,0 CALL FDOS RET ; probably doesn't need a RET! READ_CONSOLE: ; returns answer in A LD C,1 CALL FDOS RET WRITE_CONSOLE: ; prints letter in E LD C,2 ; FUNCTION NUMBER CALL FDOS RET WRITE_STRING: ; prints string at DE (ends with $) LD C,9 CALL FDOS RET WRITE_STRING_CR: ; prints string and new line at end LD C,9 CALL FDOS LD DE,CRLF LD C,9 CALL FDOS RET READ_BUFFER: ;like input in basic, reads a string from keyboard LD C,10 LD DE,READBUFF CALL FDOS RET OPEN_FILE: ; if opening a file for write to drive A etc, need to call create_file first CALL SET_DMA LD C,15 LD DE,FCB CALL FDOS RET CLOSE_FILE: LD C,16 LD DE,FCB CALL FDOS RET DELETE_FILE: LD C,19 LD DE,FCB CALL FDOS RET READ_SEQ: ; returns a=0 for success, a=1 for fail (a=1 for eof as well) ; 128 bytes at set_dma location (80H) LD C,20 LD DE,FCB CALL FDOS RET WRITE_SEQ: LD C,21 LD DE,FCB CALL FDOS RET CREATE_FILE: CALL SET_DMA LD C,22 LD DE,FCB CALL FDOS RET GET_DRIVE: LD C,25 CALL FDOS RET SET_DMA: LD C,26 LD DE,080H ; STANDARD DMA ADDRESS IS 80H CALL FDOS RET FILE_SIZE: LD C,35 LD DE,FCB ; PASS FCB CONTAINING FILE NAME CALL FDOS ; RETURNS RECORDS IN FCB+33,35,35 ; ALSO RECORDS IN CDE LD DE,FCB ; MOVE BYTES TO CDE 24 BIT NUMBER LD HL,33 ; BUT WOULD ALWAYS BE A 16 BIT NUMBER ADD HL,DE ; AS 65535*128 IS 8MB LD A,(HL) ; SO ANSWER REALLY IS DE RECORDS LD E,A INC HL LD A,(HL) LD D,A INC HL LD A,(HL) LD C,A RET ; answer in DE number of records ; ************************************************ FILL_CONTROL_Z: ; fill the record from 80H to FFH with ^Z (end of file marker) LD B,128 LD HL,80H LD A,26 ; ^z FILL1: LD (HL),A INC HL DJNZ FILL1 RET ; ************************************************ RANDOM: ; returns a number 0 to 127 in A LD A,R AND 01111111B ; bit 7 is not ever set RET ; ************************************************ ; print registers 00000 to 65535 (A is 00000 to 00255) PRINT_A: LD L,A LD H,0 CALL BINTOASCII CALL WRITE_STRING_CR RET PRINT_HL: CALL BINTOASCII CALL WRITE_STRING_CR RET PRINT_DE: EX DE,HL CALL BINTOASCII CALL WRITE_STRING_CR RET PRINT_BC: PUSH BC POP HL CALL BINTOASCII CALL WRITE_STRING_CR RET ; ************************************************ PORT_0: PUSH AF PUSH BC PUSH DE PUSH HL LD A,10 ; DELAY BEFORE CHANGE A PORT, NOT AFTER! CALL DELAY ; DELAY OF 1 WORKS, THE UDRIVE JUST NEEDS A BRIEF PAUSE LD A,00000000B ; CHANGE TO PORT 0 OUT (97),A POP HL POP DE POP BC POP AF RET PORT_1: PUSH AF PUSH BC PUSH DE PUSH HL LD A,10 ; DELAY BEFORE CHANGE A PORT, NOT AFTER! CALL DELAY LD A,01000000B ; CHANGE TO PORT 1 OUT (97),A POP HL POP DE POP BC POP AF RET PORT_2: PUSH AF PUSH BC PUSH DE PUSH HL LD A,10 ; DELAY BEFORE CHANGE A PORT, NOT AFTER! CALL DELAY LD A,10000000B ; CHANGE TO PORT 2 OUT (97),A POP HL POP DE POP BC POP AF RET PORT_3: PUSH AF PUSH BC PUSH DE PUSH HL LD A,10 ; DELAY BEFORE CHANGE A PORT, NOT AFTER! CALL DELAY LD A,11000000B ; CHANGE TO PORT 3 OUT (97),A POP HL POP DE POP BC POP AF RET ; ************************************************ SET_8255: LD A,10000001B ; A=OUT B,OUT, C HIGH=IN, CLOW=OUT OUT (99),A ; 99=CONTROL LD A,00000000B ; PORT A TO ZERO OUT (96),A ; 96=PORT A CALL DATAHIGH CALL CLOCKHIGH RET PORTCBIT4HIGH: ;bit 7=0, 654=don't care, 321 binary value of bit and 0=0 or 1 LD A,01111001B ; SEE THE 8255 DATA SHEET OUT (99),A CALL SDELAY RET PORTCBIT4LOW: LD A,01111000B ; SEE THE 8255 DATA SHEET OUT (99),A CALL SDELAY RET PORTCBIT5HIGH: LD A,01111011B ; BIT 5 HIGH OUT (99),A CALL SDELAY RET PORTCBIT5LOW: LD A,01111010B OUT (99),A CALL SDELAY RET PORTCBIT6LOW: LD A,01111100B OUT (99),A CALL SDELAY RET PORTCBIT6HIGH: LD A,01111101B OUT (99),A CALL SDELAY RET DATALOW: JP PORTCBIT4LOW ; DATA LOW ETC IS EASIER TO UNDERSTAND DATAHIGH: JP PORTCBIT4HIGH CLOCKLOW: JP PORTCBIT5LOW CLOCKHIGH: JP PORTCBIT5HIGH SDELAY: ;SHORT DELAY FOR 8MHZ AS THIS IS JUST A BIT FAST FOR THE LCD NOP NOP NOP NOP NOP NOP NOP RET ; ************************************************ ; fcb to string returns COMMAND_STRING ; eg MYPROG TEST.TXT will put TEST.TXT$ into the DE string. GET_ERROR_STRING: DB 'MISSING PARAMETER EG FILENAME.TXT$' ; at 80H=length+1 of file (0=nothing), parameter starts at 82H GET_COMMAND_STRING: LD HL,80H ; location of the parameter passed LD DE,COMMAND_STRING LD A,(HL) CP 0 JP Z,GET_COMMAND_ERROR LD HL,82H ; start counting from here FCBSTRING1: LD A,(HL) ; get the byte CP 0 ; is it zero for end of the command line JP Z,FCBSTRING2 ; yes then jump to end LD (DE),A ; no so store it INC DE ; +1 INC HL ; +1 JP FCBSTRING1 ; loop and check again FCBSTRING2: LD A,'$' ; add end of string marker LD (DE),a ; save it RET GET_COMMAND_ERROR: LD DE,GET_ERROR_STRING ; if no command entered print error CALL WRITE_STRING_CR JP WARM_BOOT ; abort program ; ************************************************ ; split the fcb string to filename$ and fileext$ ; strings_instr: ; check string de, find byte c, number returned in A ; strings_mid: ; check string de, returns in hl, b bytes starting at c ; STRINGS_LEFT: ; check string de, returns string in hl, number of bytes in B ; check for the . and use this to build the other two strings SPLIT_COMMAND: LD DE,COMMAND_STRING LD C,'.' ; find the . CALL STRINGS_INSTR ; returns A DEC A ; 1 less LD B,A LD DE,COMMAND_STRING ; use command string LD HL,FILENAME ; return filename CALL STRINGS_LEFT ; get n-1 characters LD DE,COMMAND_STRING LD C,'.' ; find the . CALL STRINGS_INSTR ; returns A INC A ; plus 1 LD C,A ; start at C LD B,3 ; 3 bytes LD DE,COMMAND_STRING ; start with command string LD HL,FILEEXT ; extension CALL STRINGS_MID ; get mid$ RET ; below does the same thing but with an arbitray string passed in DE SPLIT_STR: DW 0,0 SPLIT_STRING_TO_FILENAME: ; pass myfile.txt in DE, returns filename and fileext LD (SPLIT_STR),DE LD C,'.' ; look for . CALL STRINGS_INSTR DEC A ; 1 less LD B,A LD DE,(SPLIT_STR) ; get back the myfile.txt LD HL,FILENAME ; return filename CALL STRINGS_LEFT ; get n-1 characters LD DE,(SPLIT_STR) LD C,'.' ; find the . CALL STRINGS_INSTR ; returns A INC A ; plus 1 LD C,A ; start at C LD B,3 ; 3 bytes LD DE,(SPLIT_STR) ; start with command string LD HL,FILEEXT ; extension CALL STRINGS_MID ; get mid$ RET ; ************************************************ CONIN: ; direct console input IN A,(UART+05H); IS THERE A BYTE, 0=NO 1=YES AND 1 JP Z,CONIN ; LOOP UNTIL THERE IS IN A,(UART) ; GET THE BYTE RET ; ************************************************ CONOUT: ; used to bypass the lcd display, keyboard check etc in CP/M ; sends out byte in E (CP/M sends out C) IN A,(UART + $05) ; READ LINE STATUS REGISTER AND $20 ; TEST IF UART IS READY TO SEND JP Z,CONOUT ; IF NOT REPEAT LD A,E ; GET TO ACCUMULATOR OUT (UART),A ; THEN WRITE THE CHAR TO UART (UART0 = $68 + $00) RET ; ************************************************ CONCLEAR: ; is there a byte, if yes then get it but if no then just return IN A,(UART+05H); IS THERE A BYTE, 0=NO 1=YES AND A,1 RET Z ; no byte so return IN A,(UART) ; GET THE BYTE RET ; ************************************************ ; checks for input returns byte if there is one and B<>1 if success, B=0 if time out CONIN_TIMEOUT: LD B,100 ; loop 100 times timeout in 1sec CONIN_TIMEOUT1: IN A,(UART+05H) ; IS THERE A BYTE, 0=NO 1=YES AND 1 JP NZ,CONIN_TIMEOUT2 ; got a byte PUSH BC ; store the counter LD A,1 CALL DELAY_MILLISECOND ;0.001 second delay POP BC ; restore the counter DJNZ CONIN_TIMEOUT1 ; try 255 times LD A,0 ; return A=0,B=0 RET ; timed out. CONIN_TIMEOUT2: IN A,(UART) ; GET THE BYTE B returns the counter value RET ;----------------------------------------------- ; delay ;----------------------------------------------- ; pass A - delay is A*0.01 seconds, BCDEHL all preserved DELAY: PUSH BC ; STORE ALL VARIABLES PUSH DE PUSH HL LD B,A ; PUT THE VARIABLE DELAY IN B LD DE,1 LOOP1: LD HL,1481 ; ADJUST THIS VALUE FOR YOUR CLOCK 1481=3.68MHZ, 3219=8MHZ LOOP2: SBC HL,DE ;HL-1 JR NZ,LOOP2 DJNZ LOOP1 POP HL ; RESTORE VARIABLES POP DE POP BC RET ;----------------------------------------------- ; delay millisecond - 1 = 1 millisecond ;----------------------------------------------- ; pass A - delay is A*0.001 seconds, BCDEHL not preserved as slows things DELAY_MILLISECOND: LD B,A ; put the variable delay in B LD DE,1 DELAYM1: LD HL,148 ; adjust this value for your clock 1481=3.68Mhz, 3219=8Mhz DELAYM2: SBC HL,DE ;hl-1 JR NZ,DELAYM2 DJNZ DELAYM1 RET ; ************************************************ ; maths routines ; 1) 16 by 16 bit multiply ; 2) 32 by 16 bit divide with remainder ; 3) 8 by 8 bit multiply ; 4) 16 plus 16 bit add ; 5) 32 minus 16 bit subtract ; 6) 8 bit Comparison ; 7) Ascii to Binary ; 8) Binary to Ascii ; 9) Ascii to Hex ; 10) Hex to Ascii ; 11) 16 bit binary value to 5 ascii characters 0-9 ; 12) 5 ascii characters 0-9 to 16 bit binary ; 13) Delay routine ; 14) Floating Point 16 Bit by 16Bit Multiply 123.45 * 12.345 = 1523.99025 POWER10TABLE: DW 10000,1000,100,10,1 ; LD BC,100 ; LD DE,30 ; CALL MUL16 ; LD HL,5000 ; LD DE,0 ; LD BC,500 ; CALL DIVIDE ; LD A,30 ; LD B,100 ; CALL MUL8 ; LD DE,1000 ; LD HL,1000 ; CALL ADD16 ; LD DE,1 ; LD HL,86A0H ; LD BC,0C350H ; CALL SUB32 ; LD A,5 ; LD B,7 ; CALL COMPARE ; ORG 1100:DB "10110010":ORG 100H:LD HL,1100:CALL ASCBINARY ; LD C,0AAH:LD HL,1110:CALL BINASC ; LD D,"5" ; LD E,"F" ; CALL ASCHEX ; LD C,5FH ; CALL HEXASC ; LD IX,1000 ; LD HL,12345 ; CALL DECIMALASC ; ; ORG 1250:DB "65535":ORG 100H:LD IX,1250:CALL ASCDEC ; LD A,100 ; CALL DELAY ; LD BC,12345 ; LD DE,12345 ; LD H,2 ; LD L,3 ; CALL FLOPMUL ; RET ; 16 by 16 bit multiply BC * DE = DEHL MUL16: PUSH BC PUSH DE LD B,16 EXX POP DE POP BC LD HL,0 EX DE,HL ADD HL,HL EX DE,HL PUSH AF MUL161: POP AF JP NC,MUL162 ADD HL,BC JP NC,MUL162 INC DE MUL162: EXX DEC B EXX RET Z ; RETURN EX DE,HL ADD HL,HL EX DE,HL PUSH AF ADD HL,HL JP NC,MUL161 INC DE JP MUL161 ; 32 bit by 16 bit unsigned divide DEHL/BC = DE r = HL DIVIDE: LD A,16 EX DE,HL DIVIDE1: ADD HL,HL EX DE,HL ADD HL,HL EX DE,HL JP NC,DIVIDE2 INC HL DIVIDE2: OR A SBC HL,BC INC DE JP P,DIVIDE3 ADD HL,BC RES 0,E DIVIDE3: DEC A JP NZ,DIVIDE1 RET ; 8 bit by 8 bit unsigned multiply A*B = HL MUL8: LD L,0 LD H,A LD C,B LD B,0 LD A,8 MUL81: ADD HL,HL JP NC,MUL82 ADD HL,BC MUL82: DEC A JP NZ,MUL81 RET ; 16 plus 16 bit add DE+HL=DEHL ADD16: LD BC,0 OR A ADC HL,DE EX DE,HL LD HL,0 ADC HL,BC EX DE,HL RET ; 32 minus 16 bit subtract DEHL-BC=DEHL SUB32: OR A SBC HL,BC PUSH HL EX DE,HL LD BC,0 SBC HL,BC EX DE,HL POP HL RET ; Ascii to binary. 8 ascii 0 or 1 in memory location HL. Answer in A. ASCBINARY: LD B,8 LD C,0 ASCBIN1:SLA C LD A,(HL) INC HL SUB 30H OR C LD C,A DJNZ ASCBIN1 RET ; Binary to Ascii. Value in C to 8 ascii characters in mem HL BINASC:LD B,8 BINASC1:LD A,30H BIT 7,C JP Z,BINASC2 INC A BINASC2:LD (HL),A SLA C INC HL DJNZ BINASC1 RET ; Ascii to Hex. Ascii in DE eg ld d,"3":ld e,"B" answer 3bh or 59 in A ASCHEX: LD A,D CALL ASCHEXCVERT LD C,A LD A,E CALL ASCHEXCVERT SLA C SLA C SLA C SLA C ADD A,C RET ASCHEXCVERT: SUB 30H CP 10 RET M SUB 7 RET ; Hex to Ascii. Hex value in C. Answer in DE. HEXASC:LD A,0F0H AND C RRCA RRCA RRCA RRCA CALL HEXASC1 LD D,A LD A,0FH AND C CALL HEXASC1 LD E,A RET HEXASC1:ADD A,30H CP 3AH RET M ADD A,7 RET BINTOASCII: ; pass HL, returns answer in 5 characters at DE LD IY,POWER10TABLE LD IX,BINASCII ; ALWAYS PUT ANSWER HERE DECASC1: XOR A LD E,(IY) LD D,(IY+1) DECASC2: OR A SBC HL,DE JP C,DECASC3 INC A JP DECASC2 DECASC3:ADD HL,DE ADD A,30H ; SO ASCII VALUES LD (IX+0),A INC IX ; STORE IN IX LOCATION INC IY ; POWER10 COUNTER INC IY LD A,E CP 1 JP NZ,DECASC1 LD (IX),'$' LD DE,BINASCII ; answer in DE ready to print if needed RET ASCIITOBIN: ;location in de of 5 bytes, answer in hl 0-65535 LD B,5 LD HL,0 PUSH DE POP IX ASCDECLOOP: ADD HL,HL PUSH HL ADD HL,HL ADD HL,HL POP DE ADD HL,DE LD A,(IX) SUB 30H LD E,A LD D,0 ADD HL,DE INC IX DJNZ ASCDECLOOP RET ; 16 bit by 16 bit floating point multiply ; BC * DE = DEHL Decimal point position in H and L, answer in C FLOPMUL: PUSH HL CALL MUL16 POP BC LD A,C ADD A,B LD C,A LD B,00 RET ; ************************************************ ; test zero for words. Pass DE, returns Z flag =1 if zero. Same as CP 0 for a byte TEST_WORD_ZERO: xOR A LD BC,0 ; subtracing 0 is just so can test if hl is 0, a two byte CP 0 EX DE,HL SBC HL,BC RET ; ************************************************ ; strings len,left,mid,instr,concat,strings_add_char,clear,copy, cutup, upper STRINGS_LEN: ; de=string, a=answer LD B,0 ; COUNTER STRINGS_LEN1: LD A,(DE) CP '$' ; IS IT THE END? JP Z,STRINGS_LEN2 INC B ; +1 INC DE ; +1 JP STRINGS_LEN1 STRINGS_LEN2: LD A,B ; PUT THE ANSWER A RET STRINGS_LEFT: ; check string de, returns string in hl, number of bytes in B PUSH HL STRINGS_LEFT_1: LD A,(DE) ; GET CHARACTER LD (HL),A ; MOVE IT INC HL ; HL+1 INC DE ; DE+1 DJNZ STRINGS_LEFT_1 ; LOOP UNTIL B=0 LD A,'$' ; PUT A $ AT THE END OF THIS SHORTER STRING LD (HL),A POP HL ; RESTORE START RET STRINGS_INSTR: ; CHECK STRING DE, FIND BYTE C, NUMBER RETURNED IN A LD B,1 STRINGS_INSTR_1: LD A,(DE) CP '$' ; CHECK FOR END JP Z,STRINGS_INSTR_2 CP C ; CHECK FOR BYTE JP Z,STRINGS_INSTR_2 INC B INC DE JP STRINGS_INSTR_1 STRINGS_INSTR_2: LD A,B RET STRINGS_MID: ; CHECK STRING DE, RETURNS IN HL, B BYTES STARTING AT C PUSH HL LD H,0 LD L,C ADD HL,DE DEC HL ; 1 LESS EX DE,HL ; NEW START LOCATION IN DE POP HL STRINGS_MID1: LD A,(DE) ; MOVE THE BYTES LD (HL),A INC HL INC DE DJNZ STRINGS_MID1 LD A,'$' ; END OF STRING MARKER LD (HL),A RET ; EXAMPLE ; LD DE,STRING1 ; LD HL,STRING2 ; LD B,6 ; LD C,4 ; CALL STRINGS_MID ; LD DE,STRING2 ; CALL WRITE_STRING_CR STRINGS_CONCAT: ; STRING HL=STRING BC+DE ; MOVE STRING BC TO HL FIRST LD A,(BC) CP '$' JP Z,STRINGS_CONCAT_1 LD (HL),A INC HL INC BC JP STRINGS_CONCAT STRINGS_CONCAT_1: LD A,(DE) CP '$' JP Z,STRINGS_CONCAT_2 LD (HL),A INC HL INC DE JP STRINGS_CONCAT_1 STRINGS_CONCAT_2: LD A,'$' ; PUT MARKER AT END LD (HL),A RET STRINGS_ADD_CHAR: ;DE=STRING, A IS CHARACTER TO ADD AT END PUSH AF PUSH DE CALL STRINGS_LEN ; GET LENGTH IN A POP DE LD H,0 LD L,A ADD HL,DE ; GET LOCATION OF END OF STRING POP AF ; EG 8 CHARACTERS SO NEXT ONE LOCATION 8 LD (HL),A INC HL LD A,'$' ; PUT $ AT THE END LD (HL),A RET STRINGS_CLEAR: ;DE=STRING, CLEARS TO "" LD A,'$' LD (DE),A RET STRINGS_COPY: ; COPY STRING DE TO HL PUSH DE PUSH HL CALL STRINGS_LEN LD C,A INC C ;COPY THE $ AS WELL LD B,0 POP HL POP DE EX DE,HL ; ROUND THE RIGHT WAY FOR LDIR LDIR RET STRINGS_CUTUP: ; pass string1, returns string2, string3,string4,string5,string6 ; strings seperator is a space eg TEST1 TEST2 TEST3 TEST4 etc ; returns blank strings if nothing there ; now start at the beginning of string1 and put into string 2 LD DE,STRING1 ; start reading from string 1 LD HL,STRING2 ; start storing in string 2 CALL STRINGS_CUTUP21 LD HL,STRING3 CALL STRINGS_CUTUP21 LD HL,STRING4 CALL STRINGS_CUTUP21 LD HL,STRING5 CALL STRINGS_CUTUP21 LD HL,STRING6 CALL STRINGS_CUTUP21 RET STRINGS_CUTUP21: ; pass DE=read from this string, HL = write to this string ; puts $ at end of strings correctly LD A,(DE) CP ' ' JP Z,STRINGS_CUTUP22 ; if a space CP '$' JP Z,STRINGS_CUTUP23 ; if end of string LD (HL),A ; store the value INC DE ; increment counter INC HL ; increment counter JP STRINGS_CUTUP21 ; loop STRINGS_CUTUP22: INC DE ; returns pointer at correct position ready for next one LD (HL),'$' ; put $ at the end RET STRINGS_CUTUP23: ; when ending, don't increment, then the next one will fail too LD (HL),'$' 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 STRINGS_UPPER: ; pass string in DE, converts tup upper case PUSH DE ; store so returns the same pointer STRINGS_UPPER1: LD A,(DE) CP '$' ; is it the end JP Z,STRINGS_UPPER2 ; finish up CALL UPPER ; returns A as an upper case character LD (DE),A ; store the character INC DE ; add 1 JP STRINGS_UPPER1 ; loop STRINGS_UPPER2: POP DE ; returns DE at the beginning RET ; ************************************************ STRINGFILE1: DW 0,0 ; string to save STRING_TO_FILE: ; pass string in DE and filename in HL PUSH DE ; store location of string passed EX DE,HL ; filename to DE CALL SPLIT_STRING_TO_FILENAME ; returns filename and fileext CALL DELETE_FILE ; delete it if it exists CALL FILENAME_TO_FCB ; put it back again as delete deletes the fcb too CALL CREATE_FILE ; if making a file on the A drive, need to create it first CALL OPEN_FILE ; open the file at the fcb CALL FILL_CONTROL_Z ; fill 128 bytes with ^Z so ends when do a TYPE POP DE ; restore the string name LD HL,080H ; 128 byte buffer to save CALL STRINGS_COPY ; copy the string over CALL WRITE_SEQ ; save the record CALL CLOSE_FILE ; close the file RET ; ************************************************ ; filename manipulation ;FILENAME: DB 'MYFILE$' ;FILEPAD: DB ' $' ;FILEEXT: DB 'COM$' FILEPAD: DB ' $' FILENAME_TO_FCB: ; pass filename and fileext, puts into fcb LD HL,FILEPAD LD DE,FCB INC DE ; filename starts at byte 1 LD BC,8 ; move 8 bytes so filename is blank LDIR LD DE,FILENAME CALL STRINGS_LEN ; length of this file LD BC,0 LD C,A ; move this number of bytes to the fcb LD HL,FILENAME LD DE,FCB INC DE ; point to name =1 LDIR ; move it LD DE,FCB LD HL,9 ADD HL,DE ; point to location of ext EX DE,HL LD HL,FILEEXT LD BC,3 LDIR ; move the 3 bytes over RET END