Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

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

123 views
Skip to first unread message

Martin

unread,
Aug 20, 2022, 10:55:14 AM8/20/22
to
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< ====

Lawrence Nelson

unread,
Sep 10, 2022, 4:32:12 PM9/10/22
to
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

Martin

unread,
Sep 11, 2022, 9:48:29 AM9/11/22
to
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 ;TDL RELOCATABLE OBJECT LOADER
+ MOV A,C
+ ORA A
+ JZ L5
+ LDAX D
+ CPI '/'
+ JNZ L5 ;NO FILLER BYTE
+ INX D
+ CALL EXPR ;GET FILLER BYTE
+ POP H
+ PUSH D ;SAVE PARAMETER
+ PUSH H ;SAVE FILLER
+ 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
+ POP D ;GET FILLER
+INIMEM: MOV M,E
+ INX H
+ DCX B
+ MOV A,B
+ ORA C
+ JNZ INIMEM
+ POP D ;GET PARAMETER
+;
+L5: CALL READ ;TDL RELOCATABLE OBJECT LOADER
;
FINIS DSKI
;
==== 8< ====

dxforth

unread,
Sep 11, 2022, 10:46:50 PM9/11/22
to
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 :)


Martin

unread,
Sep 12, 2022, 1:13:12 PM9/12/22
to
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

0 new messages