• TDLLOAD: Loader for TDL object code, supports code and data segments

    From Martin@21:1/5 to All on Sat Aug 20 16:55:52 2022
    A TDL Relocatable Object Code Loader.

    Simple, stupid, and dangerous!
    It can overwrite your system, if you say so :-)

    I tried to pack all details directly into comments.
    Just assemble with the CP/M asm/load.

    Here is the source, hope someone can use it.
    Martin


    TDLLOAD.ASM:
    ==== 8< ====
    ;
    ; TDL RELOCATABLE OBJECT / INTEL HEX LOADER
    ;
    ; The loader was taken from the 8080 apple monitor
    ; and code added for CP/M file I/O using SEQIO.
    ;
    ; "APPLE MONITOR" COPYRIGHT 1975,1976,1977
    ; BY ROGER AMIDON
    ;
    ; This loader is powerful because it supports
    ; two relocation bases.
    ;
    ; It allows loading of programs with split
    ; .PROG. and .DATA. segments, as introduced
    ; in version 2.X of the TDL assembler.
    ;
    ; Command syntax:
    ; tdlload
    ; filename.ext
    ; [<loading bias>[,<rel base 1>[,<rel base 2>]]]
    ;
    ; <loading bias>
    ; is an optional bias, which will be added to
    ; the load address
    ; <rel base 1> and <rel base 2>
    ; are optional relocation bases which are used
    ; only with relocatable files.
    ;
    ; The binary output file is named filename.BIN
    ; It only contains the range of bytes loaded.
    ;
    ; "tdlload" reads a normal hex file, or a
    ; relocatable hex file. On both files, a "bias"
    ; (a shift in the address) may be added which will
    ; allow the object code to by placed in a location
    ; other than its intended execution location.
    ; The bias is added to what would hav been the
    ; normal loading location and may wrap around.
    ;
    ; When used with the TDL relocating assembler, it
    ; allows generating a program to execute anywhere,
    ; and to be stored anywhere else in memory.
    ; When loading a relocatable file, two additional
    ; parameters may be added which represent the
    ; actual execution addresses desired. This may
    ; also be any location in memory.
    ;
    ; NOTE:
    ; These values tgether with the current record
    ; offset determine the actual loading location
    ; which is used *WITHOUT* any checks!
    ;
    ; Example source with split code and data:
    ; ==== 8< ====
    ; .phex
    ; .loc .prog.
    ; start:
    ; lxi d,msg
    ; mvi c,9
    ; call 5
    ; jmp 0
    ; .loc .data.
    ; msg:
    ; .ascii 'Testing Relocation'
    ; .byte 0dh,0ah
    ; .ascii '$'
    ; .end
    ; ==== 8< ====
    ;
    ; Command to load:
    ; tdlload example.rel 1000,100,110
    ;
    ;
    BOOT EQU 0
    BDOS EQU 5
    TBUF EQU 80H
    ;
    ORG 0100H
    ;
    MACLIB SEQIO
    ;
    LXI SP,STACK
    ;
    FILE INFILE,DSKI,,1,
    ;
    LXI H,BUFFERS+@NXTB ;FREE MEMORY
    XCHG
    LHLD BDOS+1 ;TOP OF MEMORY
    XRA A ;PAGE BOUNDARY
    SUB E
    MOV C,A
    MOV A,H
    SBB D
    MOV B,A
    XCHG
    INIMEM: MVI M,0FFH ;WHATEVER
    INX H
    DCX B
    MOV A,B
    ORA C
    JNZ INIMEM
    ;
    LXI H,0 ;INIT MIN/MAX
    SHLD MMAX
    DCX H
    SHLD MMIN
    ;
    LXI H,TBUF
    MOV C,M
    INX H
    MOV A,C
    ORA A
    JZ L4
    CALL SKBLNK ;SKIP OVER BLANKS
    JZ L4
    CALL SKARG ;SKIP OVER ARGUMENT AND BLANKS
    ;
    L4: MOV D,H ;PARAMETER FOR READ
    MOV E,L
    MVI B,0 ;CR TERMINATED
    DAD B
    MVI M,CR
    ;
    CALL READ ;TDL RELOCATABLE OBJECT LOADER
    ;
    FINIS DSKI
    ;
    FILE OUTFILE,DSKO,,1,BIN
    ;
    LHLD MMIN ;START AT MIN
    SV1: LDA MMAX ;STOP AFTER MAX
    SUB L
    LDA MMAX+1
    SBB H
    JC SV2 ;DONE
    MOV A,M
    INX H
    PUSH D
    PUSH H
    PUT DSKO
    POP H
    POP D
    JMP SV1
    ;
    SV2: FINIS DSKO
    JMP BOOT
    ;
    ERROR: MVI C,'*'
    CALL CO
    ;
    FINIS DSKI
    JMP BOOT
    ;
    SKBLNK: MOV A,M ;SKIP OVER BLANKS
    CPI ' '
    RNZ
    INX H
    DCR C
    JNZ SKBLNK
    RET
    ;
    SKARG: MOV A,M ;SKIP OVER ARGUMENT AND BLANKS
    CPI ' '
    JZ SKBLNK
    INX H
    DCR C
    JNZ SKARG
    RET
    ;
    UPDMM: PUSH PSW ;UPDATE MIN/MAX
    LDA MMAX
    SUB L
    LDA MMAX+1
    SBB H
    JNC UNMAX
    SHLD MMAX
    UNMAX: LDA MMIN
    SUB L
    LDA MMIN+1
    SBB H
    JC UNMIN
    SHLD MMIN
    UNMIN: POP PSW
    RET
    ;
    READ: CALL EXPR ;GET BIAS
    LXI H,0 ;SET-UP DEFAULT BASE[1]
    PUSH H ;AND DEFAULT BASE[2]
    JC RD0 ;CR
    CALL EXPR ;GET ACTUAL BASE[1]
    POP H ;HL=BASE[1]
    JC RD0 ;CR
    XTHL ;GET DEFAULT BASE[2]
    CALL EXPR ;GET ACTUAL BASE[2]
    POP H
    XTHL ;(SP)=BASE[2]
    RD0: POP D
    POP B
    PUSH D ;BASE[2]
    PUSH H ;BASE[1]
    PUSH B ;BIAS
    JMP RD1
    ;
    RD1A: MOV A,B
    ADI ':'
    JMP RD1C
    RD1B: CALL RIX ;GET READER CHARACTER
    RD1C: CPI CR
    JNZ RD1B ;NOT CR
    CALL RIX ;GET READER CHARACTER
    CPI LF
    JNZ RD1C ;NOT LF
    RD1: CALL RIX ;GET READER CHARACTER
    SUI ':' ;GET FILE TYPE CUE
    MOV B,A ;SAVE CUE CLUE
    ANI 0FEH ;KILL BIT 0
    JNZ RD1A ;NOT ':' OR ';'
    MOV D,A ;ZERO CHECKSUM STORAGE
    CALL BYTE ;GET FILE LRNGTH
    MOV E,A ;SAVE IN E
    CALL BYTE ;GET LOAD MSB
    PUSH PSW ;SAVE IN STACK
    CALL BYTE ;GET LOAD LSB
    POP H ;H=MSB
    MOV L,A ;HL=LOAD ADDR
    CALL BYTE ;GET FILE TYPE
    ;
    ; FIX: SET THE RELOCATION BASE FOR THE RECORD
    DCR A ;TEST FILE TYPE
    JM RD2 ;M=ABSOLUTE LOAD
    PUSH D
    XCHG ;RELOCATE LOAD ADDR.
    LXI H,4 ;POINT TO BASE[1]
    JZ RD2A ;Z=BASE[1]
    INX H
    INX H ;POINT TO BASE[2]
    RD2A: DAD SP ;IN STACK
    MOV A,M
    INX H
    MOV H,M
    MOV L,A
    DAD D ;ADD BASE[N] TO LOAD
    POP D
    RD2: MOV A,B ;GET CUE
    POP B ;BC=BIAS
    ;
    INR E ;TEST LENGTH
    DCR E ;ZERO?
    JZ DONE
    DAD B ;ADD BIAS TO LOAD
    PUSH B ;SAVE BIAS
    MOV B,A ;SET-UP B
    DCR A ;TEST CUE CLUE
    JZ RD6 ;Z=REL. FILE, NZ=ABS.
    RD3: CALL BYTE ;GET NEXT DATA BYTE
    MOV M,A ;WRITE TO MEMORY
    CALL UPDMM ;UPDATE MIN/MAX
    INX H ;BUMP UP LOAD POINT
    DCR E ;BUMP DOWN BYTE COUNT
    JNZ RD3 ;CONTINUE
    RD4: CALL BYTE ;TEST CHECKSUN
    JZ RD1B ;OK; CONTINUE W/NEXT
    RD5: CALL LADR ; ELSE PRINT LOAD ADDR
    JMP ERROR ; & ABORT
    RD6: CALL RD10 ;GET NEXT DATA BYTE
    MOV M,A ;STORE IT
    CALL UPDMM ;UPDATE MIN/MAX
    JNC RD9 ;NORMAL BYTE
    PUSH H ;CARRY=RELOCATE NEXT WORD
    LXI H,5 ;POINT TO BASE[1]
    DAD SP ;IN STACK
    RD7: CALL RD10 ;GET HIGH BYTE
    JNC RD8 ;USE BASE[N]
    DCR E ;COUNT EXTRA BYTE
    XTHL ;GET LOAD ADDR
    DCR M ;TEST FOR BASE[1]
    MOV M,A ;NEW LOW BYTE
    XTHL ;SAVE LOAD AGAIN
    JZ RD7 ;BASE[1]
    INX H
    INX H ;POINT TO BASE[2]
    JMP RD7 ;AND TRY AGAIN
    ;
    RD8: ADD M ;ADD IN MSB
    XTHL
    INX H ;STICK AT LOAD+1
    MOV M,A
    DCX H ;GET LOAD BYTE
    MOV A,M ;IN A
    XTHL
    DCX H
    ADD M ;RELOCATE LSB
    POP H ;GET LOAD ADDR
    MOV M,A ;STORE IT
    CALL UPDMM ;UPDATE MIN/MAX
    INX H ;GET MSB
    MOV A,M ;IN A
    ACI 0 ;ADJUST FOR CARRY
    MOV M,A ;STORE IT
    CALL UPDMM ;UPDATE MIN/MAX
    DCR E ;COUNT IT
    RD9: INX H ;BUMP THE COUNT
    DCR E ;MORE?
    JNZ RD6 ; & CONTINUE
    JMP RD4 ;TEST CHECKSUM
    ;
    RD10: DCR B ;COUNT BITS/BYTES
    JNZ RD11 ;NEXT IS DATA BYTE
    CALL BYTE ;GET RELOC. MAP
    DCR E ;BUMP DOWN BYTE COUNT
    MOV C,A ;MAP IN C
    MVI B,8 ;RESET FOR NEXT 8
    RD11: CALL BYTE ;NEXT DATA BYTE
    PUSH D ;SAVE DE
    MOV D,A ;SAVE DATA BYTE
    MOV A,C ;TEST FOR RELOC.
    RAL ;IN CARRY FLAG
    MOV C,A ;UPDATE C
    MOV A,D ;RESTORE DATA BYTE
    POP D ;RESTORE DE
    RET ;CONTINUE
    ;
    BYTE: PUSH B ;SAVE BC
    CALL RIBBLE ;GET A CONVERTED CHAR.
    RLC
    RLC
    RLC
    RLC ;MOVE IT TO HIGH NIBBLE
    MOV C,A ;SAVE IT
    CALL RIBBLE ;GET OTHER HALF
    ORA C ;MAKE WHOLE
    MOV C,A ;SAVE IN C
    ADD D ;UPDATE CHECKSUM
    MOV D,A ;NEW CHECKSUM
    MOV A,C ;RESTORE DATA BYTE
    POP B ;RESTORE BC
    RET ;CONTINUE
    ;
    DONE: POP B ;BASE[1]
    POP B ;BASE[2]
    CALL LADR ;EXECUTION ADDRESS
    RET
    ;
    CONV: ANI 0FH
    ADI 90H
    DAA
    ACI 40H
    DAA
    MOV C,A
    RET
    ;
    EXPR: LDAX D
    INX D
    EXF: LXI H,0 ;INITIALIZE HL
    XF1: MOV B,A ;SAVE KEYBOARD
    CALL NIBBLE ;CONVERT ASCII TO HEX
    JC XF2 ;BOT LEGAL
    DAD H ;HL*16
    DAD H
    DAD H
    DAD H
    ORA L ;ADD IN NIBBLE
    MOV L,A
    LDAX D
    INX D
    JMP XF1 ;AND CONTINUE
    XF2: XTHL ;STICK PARAMETER IN STACK
    PUSH H ;REPLACE RETURN
    MOV A,B ;TEST CHARACTER
    CALL QCHK ;FOR DELIMITERS
    JNZ ERROR ;ILLEGAL
    RET
    ;
    LADR: MOV A,H
    CALL LBYTE
    MOV A,L
    ;
    LBYTE: PUSH PSW
    RRC
    RRC
    RRC
    RRC
    CALL LB
    POP PSW
    LB: CALL CONV
    JMP CO
    ;
    RIBBLE: CALL RIX
    NIBBLE: SUI '0'
    RC
    CPI 'G'-'0'
    CMC
    RC
    CPI 10
    CMC
    RNC
    SUI 'A'-'9'-1
    CPI 10
    RET
    ;
    QCHK: CPI ' '
    RZ
    CPI ','
    RZ
    CPI CR
    STC
    RZ
    CMC
    RET
    ;
    RIX: CALL RIFF
    ANI 7FH
    RET
    ;
    RIFF: PUSH B
    PUSH D
    PUSH H
    GET DSKI
    POP H
    POP D
    POP B
    CPI EOF
    JZ ERROR
    CMP D
    RET
    ;
    CO: PUSH B
    PUSH D
    PUSH H
    MOV A,C
    PUT CON
    POP H
    POP D
    POP B
    RET
    ;
    MMIN: DS 2
    MMAX: DS 2
    DS 32
    STACK:
    ;
    BUFFERS:
    END
    ==== 8< ====

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence Nelson@21:1/5 to Martin on Sat Sep 10 13:32:11 2022
    On Saturday, August 20, 2022 at 10:55:14 AM UTC-4, Martin wrote:
    A TDL Relocatable Object Code Loader.

    Simple, stupid, and dangerous!
    It can overwrite your system, if you say so :-)

    I tried to pack all details directly into comments.
    Just assemble with the CP/M asm/load.

    Here is the source, hope someone can use it.
    Martin


    TDLLOAD.ASM:
    ==== 8< ====
    ;
    ; TDL RELOCATABLE OBJECT / INTEL HEX LOADER
    ;
    ; The loader was taken from the 8080 apple monitor
    ; and code added for CP/M file I/O using SEQIO.
    ;
    ; "APPLE MONITOR" COPYRIGHT 1975,1976,1977
    ; BY ROGER AMIDON
    ;
    ; This loader is powerful because it supports
    ; two relocation bases.
    ;
    ; It allows loading of programs with split
    ; .PROG. and .DATA. segments, as introduced
    ; in version 2.X of the TDL assembler.
    ;
    ; Command syntax:
    ; tdlload
    ; filename.ext
    ; [<loading bias>[,<rel base 1>[,<rel base 2>]]]
    ;
    ; <loading bias>
    ; is an optional bias, which will be added to
    ; the load address
    ; <rel base 1> and <rel base 2>
    ; are optional relocation bases which are used
    ; only with relocatable files.
    ;
    ; The binary output file is named filename.BIN
    ; It only contains the range of bytes loaded.
    ;
    ; "tdlload" reads a normal hex file, or a
    ; relocatable hex file. On both files, a "bias"
    ; (a shift in the address) may be added which will
    ; allow the object code to by placed in a location
    ; other than its intended execution location.
    ; The bias is added to what would hav been the
    ; normal loading location and may wrap around.
    ;
    ; When used with the TDL relocating assembler, it
    ; allows generating a program to execute anywhere,
    ; and to be stored anywhere else in memory.
    ; When loading a relocatable file, two additional
    ; parameters may be added which represent the
    ; actual execution addresses desired. This may
    ; also be any location in memory.
    ;
    ; NOTE:
    ; These values tgether with the current record
    ; offset determine the actual loading location
    ; which is used *WITHOUT* any checks!
    ;
    ; Example source with split code and data:
    ; ==== 8< ====
    ; .phex
    ; .loc .prog.
    ; start:
    ; lxi d,msg
    ; mvi c,9
    ; call 5
    ; jmp 0
    ; .loc .data.
    ; msg:
    ; .ascii 'Testing Relocation'
    ; .byte 0dh,0ah
    ; .ascii '$'
    ; .end
    ; ==== 8< ====
    ;
    ; Command to load:
    ; tdlload example.rel 1000,100,110
    ;
    ;
    BOOT EQU 0
    BDOS EQU 5
    TBUF EQU 80H
    ;
    ORG 0100H
    ;
    MACLIB SEQIO
    ;
    LXI SP,STACK
    ;
    FILE INFILE,DSKI,,1,
    ;
    LXI H,BUFFERS+@NXTB ;FREE MEMORY
    XCHG
    LHLD BDOS+1 ;TOP OF MEMORY
    XRA A ;PAGE BOUNDARY
    SUB E
    MOV C,A
    MOV A,H
    SBB D
    MOV B,A
    XCHG
    INIMEM: MVI M,0FFH ;WHATEVER
    INX H
    DCX B
    MOV A,B
    ORA C
    JNZ INIMEM
    ;
    LXI H,0 ;INIT MIN/MAX
    SHLD MMAX
    DCX H
    SHLD MMIN
    ;
    LXI H,TBUF
    MOV C,M
    INX H
    MOV A,C
    ORA A
    JZ L4
    CALL SKBLNK ;SKIP OVER BLANKS
    JZ L4
    CALL SKARG ;SKIP OVER ARGUMENT AND BLANKS
    ;
    L4: MOV D,H ;PARAMETER FOR READ
    MOV E,L
    MVI B,0 ;CR TERMINATED
    DAD B
    MVI M,CR
    ;
    CALL READ ;TDL RELOCATABLE OBJECT LOADER
    ;
    FINIS DSKI
    ;
    FILE OUTFILE,DSKO,,1,BIN
    ;
    LHLD MMIN ;START AT MIN
    SV1: LDA MMAX ;STOP AFTER MAX
    SUB L
    LDA MMAX+1
    SBB H
    JC SV2 ;DONE
    MOV A,M
    INX H
    PUSH D
    PUSH H
    PUT DSKO
    POP H
    POP D
    JMP SV1
    ;
    SV2: FINIS DSKO
    JMP BOOT
    ;
    ERROR: MVI C,'*'
    CALL CO
    ;
    FINIS DSKI
    JMP BOOT
    ;
    SKBLNK: MOV A,M ;SKIP OVER BLANKS
    CPI ' '
    RNZ
    INX H
    DCR C
    JNZ SKBLNK
    RET
    ;
    SKARG: MOV A,M ;SKIP OVER ARGUMENT AND BLANKS
    CPI ' '
    JZ SKBLNK
    INX H
    DCR C
    JNZ SKARG
    RET
    ;
    UPDMM: PUSH PSW ;UPDATE MIN/MAX
    LDA MMAX
    SUB L
    LDA MMAX+1
    SBB H
    JNC UNMAX
    SHLD MMAX
    UNMAX: LDA MMIN
    SUB L
    LDA MMIN+1
    SBB H
    JC UNMIN
    SHLD MMIN
    UNMIN: POP PSW
    RET
    ;
    READ: CALL EXPR ;GET BIAS
    LXI H,0 ;SET-UP DEFAULT BASE[1]
    PUSH H ;AND DEFAULT BASE[2]
    JC RD0 ;CR
    CALL EXPR ;GET ACTUAL BASE[1]
    POP H ;HL=BASE[1]
    JC RD0 ;CR
    XTHL ;GET DEFAULT BASE[2]
    CALL EXPR ;GET ACTUAL BASE[2]
    POP H
    XTHL ;(SP)=BASE[2]
    RD0: POP D
    POP B
    PUSH D ;BASE[2]
    PUSH H ;BASE[1]
    PUSH B ;BIAS
    JMP RD1
    ;
    RD1A: MOV A,B
    ADI ':'
    JMP RD1C
    RD1B: CALL RIX ;GET READER CHARACTER
    RD1C: CPI CR
    JNZ RD1B ;NOT CR
    CALL RIX ;GET READER CHARACTER
    CPI LF
    JNZ RD1C ;NOT LF
    RD1: CALL RIX ;GET READER CHARACTER
    SUI ':' ;GET FILE TYPE CUE
    MOV B,A ;SAVE CUE CLUE
    ANI 0FEH ;KILL BIT 0
    JNZ RD1A ;NOT ':' OR ';'
    MOV D,A ;ZERO CHECKSUM STORAGE
    CALL BYTE ;GET FILE LRNGTH
    MOV E,A ;SAVE IN E
    CALL BYTE ;GET LOAD MSB
    PUSH PSW ;SAVE IN STACK
    CALL BYTE ;GET LOAD LSB
    POP H ;H=MSB
    MOV L,A ;HL=LOAD ADDR
    CALL BYTE ;GET FILE TYPE
    ;
    ; FIX: SET THE RELOCATION BASE FOR THE RECORD
    DCR A ;TEST FILE TYPE
    JM RD2 ;M=ABSOLUTE LOAD
    PUSH D
    XCHG ;RELOCATE LOAD ADDR.
    LXI H,4 ;POINT TO BASE[1]
    JZ RD2A ;Z=BASE[1]
    INX H
    INX H ;POINT TO BASE[2]
    RD2A: DAD SP ;IN STACK
    MOV A,M
    INX H
    MOV H,M
    MOV L,A
    DAD D ;ADD BASE[N] TO LOAD
    POP D
    RD2: MOV A,B ;GET CUE
    POP B ;BC=BIAS
    ;
    INR E ;TEST LENGTH
    DCR E ;ZERO?
    JZ DONE
    DAD B ;ADD BIAS TO LOAD
    PUSH B ;SAVE BIAS
    MOV B,A ;SET-UP B
    DCR A ;TEST CUE CLUE
    JZ RD6 ;Z=REL. FILE, NZ=ABS.
    RD3: CALL BYTE ;GET NEXT DATA BYTE
    MOV M,A ;WRITE TO MEMORY
    CALL UPDMM ;UPDATE MIN/MAX
    INX H ;BUMP UP LOAD POINT
    DCR E ;BUMP DOWN BYTE COUNT
    JNZ RD3 ;CONTINUE
    RD4: CALL BYTE ;TEST CHECKSUN
    JZ RD1B ;OK; CONTINUE W/NEXT
    RD5: CALL LADR ; ELSE PRINT LOAD ADDR
    JMP ERROR ; & ABORT
    RD6: CALL RD10 ;GET NEXT DATA BYTE
    MOV M,A ;STORE IT
    CALL UPDMM ;UPDATE MIN/MAX
    JNC RD9 ;NORMAL BYTE
    PUSH H ;CARRY=RELOCATE NEXT WORD
    LXI H,5 ;POINT TO BASE[1]
    DAD SP ;IN STACK
    RD7: CALL RD10 ;GET HIGH BYTE
    JNC RD8 ;USE BASE[N]
    DCR E ;COUNT EXTRA BYTE
    XTHL ;GET LOAD ADDR
    DCR M ;TEST FOR BASE[1]
    MOV M,A ;NEW LOW BYTE
    XTHL ;SAVE LOAD AGAIN
    JZ RD7 ;BASE[1]
    INX H
    INX H ;POINT TO BASE[2]
    JMP RD7 ;AND TRY AGAIN
    ;
    RD8: ADD M ;ADD IN MSB
    XTHL
    INX H ;STICK AT LOAD+1
    MOV M,A
    DCX H ;GET LOAD BYTE
    MOV A,M ;IN A
    XTHL
    DCX H
    ADD M ;RELOCATE LSB
    POP H ;GET LOAD ADDR
    MOV M,A ;STORE IT
    CALL UPDMM ;UPDATE MIN/MAX
    INX H ;GET MSB
    MOV A,M ;IN A
    ACI 0 ;ADJUST FOR CARRY
    MOV M,A ;STORE IT
    CALL UPDMM ;UPDATE MIN/MAX
    DCR E ;COUNT IT
    RD9: INX H ;BUMP THE COUNT
    DCR E ;MORE?
    JNZ RD6 ; & CONTINUE
    JMP RD4 ;TEST CHECKSUM
    ;
    RD10: DCR B ;COUNT BITS/BYTES
    JNZ RD11 ;NEXT IS DATA BYTE
    CALL BYTE ;GET RELOC. MAP
    DCR E ;BUMP DOWN BYTE COUNT
    MOV C,A ;MAP IN C
    MVI B,8 ;RESET FOR NEXT 8
    RD11: CALL BYTE ;NEXT DATA BYTE
    PUSH D ;SAVE DE
    MOV D,A ;SAVE DATA BYTE
    MOV A,C ;TEST FOR RELOC.
    RAL ;IN CARRY FLAG
    MOV C,A ;UPDATE C
    MOV A,D ;RESTORE DATA BYTE
    POP D ;RESTORE DE
    RET ;CONTINUE
    ;
    BYTE: PUSH B ;SAVE BC
    CALL RIBBLE ;GET A CONVERTED CHAR.
    RLC
    RLC
    RLC
    RLC ;MOVE IT TO HIGH NIBBLE
    MOV C,A ;SAVE IT
    CALL RIBBLE ;GET OTHER HALF
    ORA C ;MAKE WHOLE
    MOV C,A ;SAVE IN C
    ADD D ;UPDATE CHECKSUM
    MOV D,A ;NEW CHECKSUM
    MOV A,C ;RESTORE DATA BYTE
    POP B ;RESTORE BC
    RET ;CONTINUE
    ;
    DONE: POP B ;BASE[1]
    POP B ;BASE[2]
    CALL LADR ;EXECUTION ADDRESS
    RET
    ;
    CONV: ANI 0FH
    ADI 90H
    DAA
    ACI 40H
    DAA
    MOV C,A
    RET
    ;
    EXPR: LDAX D
    INX D
    EXF: LXI H,0 ;INITIALIZE HL
    XF1: MOV B,A ;SAVE KEYBOARD
    CALL NIBBLE ;CONVERT ASCII TO HEX
    JC XF2 ;BOT LEGAL
    DAD H ;HL*16
    DAD H
    DAD H
    DAD H
    ORA L ;ADD IN NIBBLE
    MOV L,A
    LDAX D
    INX D
    JMP XF1 ;AND CONTINUE
    XF2: XTHL ;STICK PARAMETER IN STACK
    PUSH H ;REPLACE RETURN
    MOV A,B ;TEST CHARACTER
    CALL QCHK ;FOR DELIMITERS
    JNZ ERROR ;ILLEGAL
    RET
    ;
    LADR: MOV A,H
    CALL LBYTE
    MOV A,L
    ;
    LBYTE: PUSH PSW
    RRC
    RRC
    RRC
    RRC
    CALL LB
    POP PSW
    LB: CALL CONV
    JMP CO
    ;
    RIBBLE: CALL RIX
    NIBBLE: SUI '0'
    RC
    CPI 'G'-'0'
    CMC
    RC
    CPI 10
    CMC
    RNC
    SUI 'A'-'9'-1
    CPI 10
    RET
    ;
    QCHK: CPI ' '
    RZ
    CPI ','
    RZ
    CPI CR
    STC
    RZ
    CMC
    RET
    ;
    RIX: CALL RIFF
    ANI 7FH
    RET
    ;
    RIFF: PUSH B
    PUSH D
    PUSH H
    GET DSKI
    POP H
    POP D
    POP B
    CPI EOF
    JZ ERROR
    CMP D
    RET
    ;
    CO: PUSH B
    PUSH D
    PUSH H
    MOV A,C
    PUT CON
    POP H
    POP D
    POP B
    RET
    ;
    MMIN: DS 2
    MMAX: DS 2
    DS 32
    STACK:
    ;
    BUFFERS:
    END
    ==== 8< ====
    I also made a CP/M version of TDLoader. I'll try to compare our two versions of the code. Been a while since I used it mainly for making a CP/M version of TDL Basic.

    Lars

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Martin@21:1/5 to Lawrence Nelson on Sun Sep 11 15:48:32 2022
    Am 09/10/2022 10:32 PM, Lawrence Nelson schrieb:
    On Saturday, August 20, 2022 at 10:55:14 AM UTC-4, Martin wrote:
    A TDL Relocatable Object Code Loader.

    Simple, stupid, and dangerous!
    It can overwrite your system, if you say so :-)

    I tried to pack all details directly into comments.
    Just assemble with the CP/M asm/load.

    Here is the source, hope someone can use it.
    Martin


    I also made a CP/M version of TDLoader.
    I'll try to compare our two versions of the code.
    Been a while since I used it mainly for making a CP/M version of TDL Basic.

    Lars


    It all began with /ENTERPRS/CPM/UTILS/S/Z80MAC.ZIP
    on the CP/M CD-ROM.

    I realized, it likely is based on an older version of
    TASMIO (CP/M-UG #16) and my reverse engineering interest
    was awake... :-)

    Soon I was analysing the CP/M binaries floating around
    and relate them with the existing CP/M adapters, like
    MAC4.ASM, MAC6.AZM and TASMIO.MAC (CP/M-UG #8, #36 and #16).


    Couldn't believe that there is absolutely no small CP/M loader
    for the TDL RELOCATABLE OBJECT format in the CP/M archives.

    So I wrote this patchwork, tried to reuse working code and
    to keep the memory footprint small.


    The first version was hardcoded to always prefill with 0FFH.
    But in the meantime, I realized the need to a prefill with 000H,
    so the following small modification was made.

    It allows you to prepend a prefill value starting with a '/'.
    This allowes to overlay TDL code by means of running the loader
    multiple times. For now, just erase the intermediate BIN files.


    NOTE: I fixed one *SEVERE BUG* of this loader!

    The default relocation type of the record was ignored.
    All bytes without explicit relocation info were relocated
    relative to the ".CODE." segment.

    Look in the code after this line:
    ; FIX: SET THE RELOCATION BASE FOR THE RECORD


    Use this "unified diff", or hand-apply the diffs to the source.


    Have fun!
    Martin


    ==== 8< ====
    --- tdlloadO.asm
    +++ tdlload.asm
    @@ -17,8 +17,10 @@
    ; Command syntax:
    ; tdlload
    ; filename.ext
    -; [<loading bias>[,<rel base 1>[,<rel base 2>]]]
    +; [/<filler>,][<loading bias>[,<rel base 1>[,<rel base 2>]]]
    ;
    +; /<filler>
    +; if specified, prefill memory with filler
    ; <loading bias>
    ; is an optional bias, which will be added to
    ; the load address
    @@ -83,23 +85,6 @@
    ;
    FILE INFILE,DSKI,,1,
    ;
    - LXI H,BUFFERS+@NXTB ;FREE MEMORY
    - XCHG
    - LHLD BDOS+1 ;TOP OF MEMORY
    - XRA A ;PAGE BOUNDARY
    - SUB E
    - MOV C,A
    - MOV A,H
    - SBB D
    - MOV B,A
    - XCHG
    -INIMEM: MVI M,0FFH ;WHATEVER
    - INX H
    - DCX B
    - MOV A,B
    - ORA C
    - JNZ INIMEM
    -;
    LXI H,0 ;INIT MIN/MAX
    SHLD MMAX
    DCX H
    @@ -121,7 +106,37 @@
    DAD B
    MVI M,CR
    ;
    - CALL READ
  • From dxforth@21:1/5 to Martin on Mon Sep 12 12:46:49 2022
    On 11/09/2022 11:48 pm, Martin wrote:
    ...
    Couldn't believe that there is absolutely no small CP/M loader
    for the TDL RELOCATABLE OBJECT format in the CP/M archives.

    Perhaps not that much interest given CP/M apps generally ORG'd at
    $100 and TDL ZAPPLE was from an earlier era. Nevertheless it's
    impressive what Colvin achieved in such a short space of time.

    Nice work on your program! I wrote a simple TDL REL V1 to binary
    app sometime ago as a fun exercise. Given your efforts, I can
    forego adapting it to V2 :)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Martin@21:1/5 to dxforth on Mon Sep 12 19:13:11 2022
    Am 09/12/2022 04:46 AM, dxforth schrieb:
    On 11/09/2022 11:48 pm, Martin wrote:
    ...
    Couldn't believe that there is absolutely no small CP/M loader
    for the TDL RELOCATABLE OBJECT format in the CP/M archives.

    Perhaps not that much interest given CP/M apps generally ORG'd at
    $100 and TDL ZAPPLE was from an earlier era. Nevertheless it's
    impressive what Colvin achieved in such a short space of time.

    Nice work on your program! I wrote a simple TDL REL V1 to binary
    app sometime ago as a fun exercise. Given your efforts, I can
    forego adapting it to V2 :)



    I would not call the loader V2, it just marks the very beginning
    of the transition to V2, namely the (buggy) addition of another
    relocation base.

    I first took this loader from the ETC 8080 Apple monitor,
    because I was interested in the clever programming needed to solve
    the same tasks without having the additional features of the Z80.


    While adjusting the paramter parser, I realized that the loader supports
    an additional relocation base (.DATA.) and that this wasn't implemented anywhere else.

    It's a fascinating example of what an experienced programer can do with
    the 8080, just by playing a little bit with its registers and the stack!


    I deliberately kept the loader 8080 only, so that it can be used on all
    CP/M systems.

    Have fun!
    Martin

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)