• Please review our updated Terms and Rules here

Turbo Pascal 3 Library Routines

CP/M User

Veteran Member
Joined
May 2, 2003
Messages
2,986
Location
Back of Burke (Guday!), Australia
I was just wondering if anyone here has had a crack at understanding this vast Library routines which attaches itself to Turbo Pascal 3 "COM" files (specifically in the 8bit version of Turbo Pascal)?

Here's what I've come up with - basically before your TP program is executed some things are setup - what they are I can only guess they have something to do with where Data and other bits 'n' blobs go - some of which looks as if engages with CP/M since some CP/M addresses (below 100h) are used - perhaps it has something to do with Parameter passing?

Execution from 0100 (since this is where COM files execute from) takes you to 20E2 and you get these routines

Code:
20E2: LD SP,0100h
LD HL,93C2h
LD BC,FF00h
CALL 364h
LD HL,222Bh
LD DE,9BA8h
LD BC,9C42h
LD A,01
CALL 04D4h
JP <Start of Main Pascal Program>

Code:
364: LD (00D2h),HL
LD A,B
LD (00DDh),A
LD A,C
OR A
JR Z,037Ah
LD A,C3h
LD (0038h),A
LD HL,1FFBh
LD (0039h),HL
LD HL,03A5h
LD DE,00A0h
LD BC,0018h
LDIR
LD HL,03BDh
LD DE,00B8h
LD BC,000Ch
LDIR
XOR A
LD L,A
LD H,A
LD (00D0h),A
LD (00D4h),HL
LD (00D6h),HL
LD A,7Eh
LD (00D1h),A
LD (00E0h),A
RET

This code above at 364 seems to be very critical for my program cause eliminating it crashed the program. I've disassembled this following routine though I managed to run my program without - that's not to imply all my programs will work without it, I just thought if anyone had any ideas what it might be doing that would be great. I'm not posting the complete think or at least I don't think I am, if anyone wants the routines which it's Jumping & calling to I try and get those (unfortunately there's a 'JP (HL)' statement in there as well and I'm unsure where that ones going - I hate JumPs so much :-x)

Code:
4D4: LD (00D8h),A
PUSH BC
CALL 1EAFh
POP BC
LD HL,(0006h)
OR A
SBC HL,BC
JP C,20A8h
EX DE,HL
POP DE
LD SP,HL
LD BC,FC00h
ADD HL,BC
LD (00C6h),HL
XOR A
LD L,A
LD H,A
LD (00CEh),HL
LD (00DCh),A
LD A,C3h
LD (00D9h),A
LD HL,20DEh
LD (00DAh),HL
EX DE,HL
LD (00CCh),HL
JP (HL)
PUSH HL
LD HL,(00C6h)
OR A
SBC HL,BC
LD (00C6h),HL
LD DE,(00C4h)
OR A
SBC HL,DE
ADD HL,DE
EX DE,HL
POP HL
JP C,1D75h
LDIR
RET

Any help or comments would be great. I seem to have highlighted the core which is processed prior to program (well in part - seems amazing there's so much code prior to my program being executed, I prepared another program in Turbo Pascal using Inline M/C - just to get a program running outside of CP/M and in AMSDOS on my CPC, the result was a very limited program which I had running outside of CP/M, though CP/M seems to cling on for dear life to that TP Library. This seems more complicated, once I'm into my program there are calls to the library the first - 666 looks to be it's pointing to a 'FOR' routine which was the first thing I wrote in my Main Routine, followed by Calls to other Procedures which I did.

What I'm trying to do is understand the process so then I can start removing what I don't need and create a small executable program. Don't think I'll be able to make a program which allows you to do this - and I think the easiest way to make the program smaller is to simply move the program to where it sits - 20E2 (or whatever). Different programs will obviously have different routines and it would have to be a process of removing and leaving the ones which are used.
 
Last edited:
Okay one thing I notice with the 4D4 routine is the values setup before that vary from Program to Program - 222Bh in HL for example is where the start of Free Memory starts - this is 31101bytes in size for the program I compiled. The Value in DE 9BA8h being the last byte of Free Memory - after that memory is assigned for Data - which is between 9BA9h and 9C42h (which curiously enough is the value in BC), the Acculator has the value assigned to it and then that Routine is called (whatever it is).

The more vital routine at 364 definitely moves some Data around (I can tell since LDIR is used) - this moves stuff into A0 and just finishing before 0100h. Between A0 and B8 a series of JPs are used (which play some kind of role for the library routines), the second lot of information used between B8h and C4h looks like some kind of values assigned - I'm not sure if these vary from program to program (I'll have to check), though I'm guessing they will be the same. This low region of memory is used by CP/M, though for some reason Borland have decided that nobody will be listing an Directory whilst running a TP program - the area is purely reserve for storing Directory program names (cheeky! 8-o)
Some other values from the 364 routine are also poked into various regions of this memory (e.g. 38h, 39h, D0h, D1h, D4h, D6h, DDh, E0h).
 
Okay I've dug deeper into checking this 4D4 routine, it appears to be some sort of error handler which aborts if the Computer doesn't have enough memory or the program has been aborted or something.

As part of the 4D4 routine, another routine is executed (at 1EAF) which looks like this:

Code:
 1EAF: LD (00C4),HL
LD (00DE),HL
LD B,04
loop: LD (HL),00
INC HL
DJNZ loop
RET

A Jump is done if there's a Carry to 20A8 - which does a CALL to 200 which looks like:

Code:
0200: PUSH HL
LD HL,01E8
LD (0213),HL
POP HL
EX (SP),HL
PUSH SP
PUSH BC
PUSH DE
020C: LD A,(HL)
INC HL
OR A
JR Z,218 
PUSH HL
CALL 01E8
POP HL
JR 020C
218:POP DE
POP BC
POP SP
EX (SP),HL
RET

This is all terribly confusing to me, though just before the call to 200, there's some error messages stating memory is full or errors of that effect and eventually aborts to CP/M. There's also a call to 01E8 which looks like this:

Code:
01E8: LD L,A
PUSH HL
CALL A6
RET

The call to A6 is a jump to JP 339 which is one of the JPs setup from the first routine at call to 364. Basically I'm guessing the 4D4 routine wasn't critically vital on this occassion because my program worked fine. The 4D4 routine is still doing stuff later before RETurning where it was called I'm guessing it's more error related stuff though some DATA is moved before RETurning where it came from (no doubt), unsure in what relation this might have - I'll have to check see whate that JP on C to 1D75 is doing! :-o

I'm a bit 50/50 about including this error handling routine, I'm guessing if I can produce a tight "COM" file with that working in it - though I'm also worried that if I cannot get it to produce an error I won't know if it really works or not! :-o
 
Last edited:
Eek I seemed to have hit a brick wall with this routine

Code:
JP C,1D75h

which seems to point to heaps and heaps of code which needs sorting out. Some Error Handler - found more error messages such as Run-time Errors and User Break messages in case someone wants to Ctrl-C out! :-o

I'm more tempted to skip this part and jump to the program - it's more likely gonna run without Error Checking cause the program will be smaller and people who wanna get out of a CP/M program using [CTRL]+C should know better! :-o
If I leave it out though, people will complain my programs aren't protecting the SP from filling! :-x

The Worst thing about this section of code is this Conditional JP takes you to another conditional Jump and suddenly it becomes very hard to follow (like a miserable BASIC program loaded to the brim with GOTOs - conditional Jumps are mearly "IF this do that and GOTO here!" :-x ) though worse in Assembly since one routine sits against another in memory - yep they couldn't even seperate a routine with 3 miserable bytes of 00 back in them days! :-x

CP/M User.
 
Understanding Jump (JP) and Jump Relative (JR)

Understanding Jump (JP) and Jump Relative (JR)

I've been trying to understand the concept of Jump and Jump Relative, from what I already know Jump Relative (JR) are relatively short jumps - with the idea here that they jump within 128 bytes foward or backwards. Because of this "JR" use less Opcodes than a traditional Jump and like an ordinary Jump can be used as a conditional Jump - Jump Relative is usually found in a looping situation (not all the time, though it's one place your likely to find them!). A tradional Jump is handy though if you have large routines and need to go from one spot to another.

What confused me is when you do a CALL to "this routine" and "this routine" starts Jumping around. Yes Jumps can be conditional, though when extracting parts of the Turbo Pascal Library it becomes tricky to judge which bits of code are used and other parts are used later for example - cause in some routines I've found Conditional RETurns. In fact what should occur is whenever routines are written they need to finish with a RET statement - this is the big assumption I'm guessing Borland have done here.

Jumps and Jump Relatives like I said earlier are simply Assembly equivalant of GOTO - a conditional Jump (whatever) is almost like saying "ON variable routine GOTO this line, that line".

As a note to myself, "RET statments have no bearing on Jumps whatsoever", if a CALL was made and a RET statment is found after piles and piles of Jump routines then that RET will return to the point after that last CALL made - this is where I believe a routine would finish.
 
CP/M User:

I am not very well versed on Z80/8080/8085 ASM or CP/M programming, so I know I won't be much help here, but let me toss out a couple of things to see if I am on the same page as you:

All of my previous Assembly Language programming experience is on MS-DOS. When I create a COM file under MS-DOS, only my code goes into it, nothing else. You can load the finished COM file into debug and see that only your code is in there. The difference is, I think, that with MS-DOS, you make calls to DOS (and BIOS) interrupts to handle most of the OS-specific processing. These are not linked in to your program. Does CP/M do the same thing, or does TP-3 have to link-in specific libs to handle that functionality?

As for the jump/jump relatives, I'm afraid that is part and parcel with Assembly Language. Which is one reason it is imperative to comment your code! ;-) Since comments don't come with the assembled libs, disassembling the program (or lib) leaves you with a mess on your hands. Which is another reason I try not to disassemble programs!
 
DoctorPepper wrote:

CP/M User:

I am not very well versed on Z80/8080/8085 ASM or CP/M programming, so I know I won't be much help here, but let me toss out a couple of things to see if I am on the same page as you:

All of my previous Assembly Language programming experience is on MS-DOS. When I create a COM file under MS-DOS, only my code goes into it, nothing else. You can load the finished COM file into debug and see that only your code is in there. The difference is, I think, that with MS-DOS, you make calls to DOS (and BIOS) interrupts to handle most of the OS-specific processing. These are not linked in to your program. Does CP/M do the same thing, or does TP-3 have to link-in specific libs to handle that functionality?

No Unfortunately I have read it somewhere on the 'net (and I might have even read it in the TP3 Manual) that COM files (produced in TP3) have Library functions included in those files, regardless of whatever version of TP3 you're using - MS-DOS, CP/M, or the PC-DOS version. I mention the PC-DOS version because the MS-DOS version is more generic (I haven't used it so I cannot tell you though), the PC-DOS version generates the largest of the COM files due to the extra routines bundled within it (which are IBM/ IBM Compat. specific) - there is also an additional library file (call graph.lib or something) which needs to be included in case you want to use additional graphics - though that's straying off the subject! :-o

The only files which doesn't include the library routines with TP3 are CHN files - and they are a lot smaller than your COM files. In order to run them though you need to use the CHAIN statement in a TP3 file (which ironically needs to be a COM file! ;-) Otherwise the TP3 compiler tells me where my code is (which also follows a simular path in the PC-DOS version) which typically locates itself past the library routines! :-o
COM files always start at 0100 (even on a IBM compat.) though my code is executed at 20E2 or whatever! So packed away in there is a whole library! :-o

As for the jump/jump relatives, I'm afraid that is part and parcel with Assembly Language. Which is one reason it is imperative to comment your code! ;-) Since comments don't come with the assembled libs, disassembling the program (or lib) leaves you with a mess on your hands. Which is another reason I try not to disassemble programs!

I can understand the purpose of commenting - I felt compelled to at least have a crack at a list of Library routines to make my program a lot smaller. I can definitely say for sure that my code simply "CALLs" routines which are required, by default an unconditional call should have an unconditional "RET" - if I do a "Conditional CALL" than a "Conditional RET" maybe required, I will have to go back and start looking at this "WRITE" routine - which will probably be one of the more complicated routines TP will have within it's Library. The best way I know how to attack this is to associate call number addresses with routines, those routines will be Fixed at those addresses (though once I've been through it all - they may change). I've got my TP Source Code of the program I'm trying to crack which also helps a lot too! :-D
 
A couple of years back (well, maybe a few years, by now) I disassembled and started commenting an 8085 assembly language program. I used a commercial disassembler, and ended up with 17,000+ lines of code and 1500 or so detected labels. This is one of those "in my spare time" type of projects. I'd say I'm about 20% done with the commenting and I'm up to almost 30,000 lines in the source code file.

The 8085 doesn't have the JR, if I recall...I think that was a Z80 extension. As far as I remember, the only difference was that JUMP takes a value and adds it to 0 (or the ORG statement) and then replaces PC with that value, whereas JR takes the same value and adds it to the value in PC then replaces PC. Correct me if I'm wrong...I deal with Z80 cpus very little.
 
SwedaGuy wrote:

A couple of years back (well, maybe a few years, by now) I disassembled and started commenting an 8085 assembly language program. I used a commercial disassembler, and ended up with 17,000+ lines of code and 1500 or so detected labels. This is one of those "in my spare time" type of projects. I'd say I'm about 20% done with the commenting and I'm up to almost 30,000 lines in the source code file.

The 8085 doesn't have the JR, if I recall...I think that was a Z80 extension. As far as I remember, the only difference was that JUMP takes a value and adds it to 0 (or the ORG statement) and then replaces PC with that value, whereas JR takes the same value and adds it to the value in PC then replaces PC. Correct me if I'm wrong...I deal with Z80 cpus very little.

Unsure to be honest cause I've never played with an 8085 processor. On my Z80 computer the JP is typically 3 bytes in size with the last two bytes representing the address to Jump to. A JR only has 2 bytes - it can Jump foward 128 bytes (&FF) and backwards 127, I generally have a hard time recalling playing around with a JR statement when poking around with Bytes - which is why I'm going to dig out an assembler and label everything which needs labelling - saves the confusion of know if I need to jump &0e bytes of &0f bytes foward or backward (backwards is generally a lot more confusing - fowards is generally the next byte - &01 byte away! :-D)

I wasn't sure if you were using TP3 on an 8085 - I was under the impression it generated Z80 code - which begs the question if the TP library is using 'JR' instructions what is the byte equivalent of a 'JR' on an 8085?
The only possible answer I can explain is the version of TP I'm using for my CPC based Amstrad appears to be custom made - I tried installing a generic copy of TP on that machine, though hit problems with the console layout - perhaps the library has been reworked as Z80 based. The only other guess I can make is there are a number of variations of TP3 floating around - in terms of all the TP compilers made 3 seems to be the broadest found on a wide range of platforms.
 
there are a number of variations of TP3 floating around - in terms of all the TP compilers made 3 seems to be the broadest found on a wide range of platforms.[/FONT]

TP3 was the last version produced that ran in multiple environments.

It's not clear from the original post what you're trying to do. Are you simply trying to understand what the library stub does, or are you trying to reduce the size of the program by eliminating library code that isn't called?
 
Trixter wrote:

TP3 was the last version produced that ran in multiple environments.

It's not clear from the original post what you're trying to do. Are you simply trying to understand what the library stub does, or are you trying to reduce the size of the program by eliminating library code that isn't called?

Put simply I was trying to understand some of the inital code which sets up the Turbo Pascal program, the second routine I believe comes into play when an error has been detected - it could be a runtime error or a error in relation to the amount of memory your system has, etc. The first bit of code which was more crutical for my program seems to move some data around and from memory it moves a series of JP routines into an area which CP/M uses for Displaying the DIRectory contents! :-o

I did establish where my code was and what I had in mind was to extract the library routines used and reassemble them into a small program - at the same time I also wanted to keep those routines and simply include them as I need them for a program - though I wanted to get this other stuff as well - particularly this information which gets moved around.

I should clarify though that I'm using the 8bit version of TP in this case - there is a website which extracts the IBM version (PC-DOS) of TP though which relates to the Code Generation and Internals.
 
Back
Top