FORMAT (? BDSCIO H .BDS LIB) CC1 COM^CC2 COMv !"#$%&'(CLINK COM!)*+,-C CCC ./CLIB COM(01234CMAC LIBR56789:;<=>?CCC ASM@ABCDEFGHIJKLMNOCCC ASM;PQRSTUVWCONVERT C XYZ[CC0T C \]DEFF CRL=^_`abcdeDEFF2 CRL,fghijkDEFF2 ASMlmnopqrstuvwxyz{DEFF2 ASMg|}~/* The BDS C Standard I/O header file -- v1.42 November 22, 1980 This file contains global definitions, for use in all C programs in PLACE of (yechhh) CONSTANTS. Characteristics of your system such as video screen size, interface port numbers and masks, buffered I/O allocations, etc., should all be configured just once within this file. Any program which needs them should contain the preprocessor directive: #include "bdscio.h" near the beginning. Go through and set all this stuf"\033E" /* String to clear screen on console */ #define INTOREV "\033p" /* String to switch console into reverse video */ #define OUTAREV "\033q" /* String to switch console OUT of reverse video */ #define CURSOROFF "\033x5" /* String to turn cursor off */ #define CURSORON "\033y5" /* String to turn cursor on */ #define ESC '\033' /* Standard ASCII 'escape' character */ /******* Console serial port characteristics: ******/ #define CSTAT 0 /* Console status port */ #define CDATA 1 /* Console dsk */ #define MOMASK 0x80 /* Modem ready to send a character mask */ #define MAHI 1 /* True if modem status logic active high */ #define MRESET 0 /* True if modem status port needs to be reset */ #define MRESETVAL 0 /* If MRESET true, this is the byte to send */ /****** General purpose Symbolic constants: ******/ #define BASE 0 /* Base of CP/M system RAM (0 or 0x4200) */ #define NULL 0 /* Used by some functions to indicate zilch */ #define EOF -1 /* Physical EOF returned by low level I/O DEFF2A ASMNFLOAT DOC3FLOAT C 9FLOATSUMC f as soon as you get the package, and most terminal-dependent sample programs should run much better. Some games (such as STONE.C and RALLY.C), which were contributed and beyond the scope of my ablity (or patience) to generalize, may not bother to use the globals from this file, alas. */ /******* Some console (video) terminal characteristics: ******* ******* (pre-configured for H19/Z19) *******/ #define TWIDTH 80 /* # of columns */ #define TLENGTH 24 /* # of lines */ #define CLEARS ata port */ #define CIMASK 0x40 /* Console input data ready mask */ #define COMASK 0x80 /* Console output data ready mask */ #define CAHI 1 /* True if console status active high */ #define CRESET 0 /* True if status port needs to be reset after input */ #define CRESETVAL 0 /* If CRESET is true, this is the value to send */ /******* Modem characteristics: ******/ #define MSTAT 2 /* Modem status port */ #define MDATA 3 /* Modem data port */ #define MIMASK 0x40 /* Modem input data ready mafunctions */ #define ERROR -1 /* General "on error" return value */ #define OK 0 /* General purpose "no error" return value */ #define CPMEOF 0x1a /* CP/M End-of-text-file marker (sometimes!) */ #define SECSIZ 128 /* Sector size for CP/M read/write calls */ #define MAXLINE 135 /* Longest line of input expected from the console */ #define TRUE 1 /* general purpose true truth value */ #define FALSE 0 /* general purpose false truth value */ /******* Number of sectors to use for buffered I/O: ****** The NSECTS symbol controls the compilation of the buffered I/O routines within STDLIB2.C, allowing each user to set the buffer size most convenient for his system, while keeping the numbers totally invisible to the C source programs using buffered I/O (via the BUFSIZ defined symbol.) For larger NSECTS, the disk I/O is faster...but more ram is taken up. Note that prior (pre 1.4) versions of the library functions were not set up to support this customizable buffer size, #include "bdscio.h" in your programs!) ****************************************************************/ #define NSECTS 8 /* Number of sectors to buffer up in ram */ #define BUFSIZ (NSECTS * SECSIZ + 6 ) /* Don't touch this */ struct _buf { /* Or this... */ int _fd; int _nleft; char *_nextp; char _buff[NSECTS * SECSIZ]; }; /**************************************************************************** If you plan to use the high-level storage allocation functions from theON!! Remember to include bdscio.h in ALL files of your C program. The lack of static variables is the reason for all this messiness. ****************************************************************************/ #define ALLOC_OFF 1 /* disables storage allocation if uncommented */ /* only ONE of these two lines should be uncommented */ /* #define ALLOC_ON 1 /* enables storgage allocation if uncommented */ */ #ifdef ALLOC_ON /* if storage allocation enabled, */ struct _header { ; ; BDS.LIB for BDS C v1.41 October 14, 1980 ; ; Addresses within C.CCC and the ram area to be used by machine ; language CRL functions. ; ; If you alter C.CCC by reassembling CCC.ASM, be sure to go through ; this file and make sure all the addresses are equated to the ; appropriate values resulting from the reassembly. Then the library ; functions will be ready to reassemble. ; page 76 CPM: EQU 1 ;true if running under CP/M; else 0 ; ; System addresses: ; if not cpm CCy point tpa: equ base+100h ;transient program area nfcbs: equ 8 ;max number of open files allowed at one time errorv: equ 255 ;error value returned by BDOS calls cccorg: equ tpa ;where run-time package resides *TESTING* ;************************************************** ram: equ cccorg+471h ;THIS WILL PROBABLY CHANGE IF YOU CUSTOMIZE CCC.ASM ;************************************************** endif cr: equ 0dh ;ASCII codes: carriage return lf: equ 0ah ; linefeed newlin: equ lf and always compiled as if NSECTS was 1 in this version. To change the buffer size allocation, follow these steps: 1) Alter NSECTS to the desired value here in bdscio.h 2) Re-compile STDLIB1.C and STDLIB2.C 3) Use CLIB to combine STDLIB1.CRL and STDLIB2.CRL to make a new DEFF.CRL. Make sure you use declare all your I/O buffers with the a statement such as: char buf_name[BUFSIZ]; instead of the older and now obsolete: char buf_name[134]; (and always library ("alloc" and "free") then: 1) Uncomment (enable) the "ALLOC_ON" definition, and comment out the "ALLOC_OFF" definition from this file. 2) Re-compile STDLIB1.C, and use CLIB to transfer "alloc" and "free" into the DEFF.CRL library file. 3) THIS IS IMPORTANT!!! Include the statement: _allocp = NULL; /* initialize allocation pointer */ somewhere in your "main" function PRIOR to the first use of the "alloc" function. DON'T FORGET THIS INITIALIZATI struct _header *_ptr; unsigned _size; }; struct _header _base; /* declare this external data to */ struct _header *_allocp; /* be used by alloc() and free() */ #endif storage allocation if uncommented */ /* only ONE of these two lines should be uncommented */ /* #define ALLOC_ON 1 /* enables storgage allocation if uncommented */ */ #ifdef ALLOC_ON /* if storage allocation enabled, */ struct _header {CORG: EQU WHATEVER ;IF NOT RUNNING UNDER CP/M, SET THIS TO LOAD ADDR, RAM: EQU WHATEVER2 ;SET THIS TO RAM AREA, BASE: EQU WHATEVER3 ;AND THIS TO THE BASE OF SYSTEM MEMORY (`BASE' IS ;THE RE-BOOT LOCATION UNDER CP/M; FOR NON-CP/M OPER- ;ATION, IT SHOULD BE SET TO A SAFE PLACE TO JUMP TO ON ;ERROR OR USER-ABORT. endif if cpm base: equ 0000h ;either 0 or 4200h for CP/M systems fcb: equ base+5ch ;default file control block tbuff: equ base+80h ;sector buffer bdos: equ base+5 ;bdos entr ; newline tab: equ 9 ; tab bs: equ 08h ; backspace cntrlc: equ 3 ; control-C ; ; Subroutines in C.CCC (the addresses should be that of the ; appropriate jump vector entry points): ; error: equ cccorg+1dh ;return -1 in HL: exit: equ error+3 ;close all open files and reboot if cpm close: equ error+6 setfcb: equ error+9 ;set up fcb at HL from text at DE fgfd: equ error+12 ;set C according to whether file fd is open fgfcb: equ error+15 ;figure address of internal fcb for file fd endif eqwel: equ cccorg+0e5h smod: equ cccorg+10fh usmod: equ cccorg+129h smul: equ cccorg+13fh usmul: equ cccorg+16bh usdiv: equ cccorg+189h sdiv: equ cccorg+1cbh ma1toh: equ cccorg+20ah ;get 1st stack element into HL and A ma2toh: equ cccorg+213h ; 2nd ma3toh: equ ma2toh+6 ; 3rd ma4toh: equ ma2toh+12 ; 4th ma5toh: equ ma2toh+18 ; 5th ma6toh: equ ma2toh+24 ; 6th ma7toh: equ ma2toh+30 ; 7th arghak: equ ma2toh+36 ;copy first 6 or so stack elements to argc area setm) pbase: ds 2 ;DMA video plotting base ysize: ds 2 ;screen width (# of columns) xsize: ds 2 ;screen length (# of lines) psize: ds 2 ;screen size (ysize * xsize) rseed: ds 8 ;random number seed scratch area args: ds 14 ;where arghak puts arg values off the stack iohack: ds 6 ;room for input and output ops for "inp" and "outp" allocp: ds 2 ;storage allocation pointer alocmx: ds 2 ;highest addr useable by storage allocator ; ; This is the end of the user-customizable area. The 2 ;the words in the "args" area arg3: equ args+4 arg4: equ args+6 arg5: equ args+8 arg6: equ args+10 arg7: equ args+12 tmp: equ room ;some scratch data areas used by library tmp1: equ room+1 ;functions. tmp2: equ room+2 tmp2a: equ room+4 ungetl: equ room+6 lastc: equ room+7 ; ; BDOS call codes: ; if cpm conin: equ 1 ;get a character from console conout: equ 2 ;write a character to console lstout: equ 5 ;write a character to list device dconio: equ 6 ;direct console I/O (only cfsizc: equ 35 ;compute file size srrecc: equ 36 ;set random record endif  ;get console status select: equ 14 ;select disk openc: equ 15 ;open a file closec: equ 16 ;close a file delc: equ 19 ;delete a file reads: equ 20 ;read a sector (sequential) writs: equ 21 ;write a sector (sequential) creatc: equ 22 ;make a file renc: equ 23 ;rename file sdma: equ 26 ;set dma readr: equ 33 ;read random sector writr: equ 34 ;write random sectorE Copyright (c) 1980 by Leor Zolman Please don't rip me off. 1^:\2~22222222}22<22|22><2! "!"!H! ":Ɓo&6ͮ!( ʫ͸ͮ"-ͮS2üO2üP2}üR ڂg."üA͛22üD0͛ʂ2üMAaڂ"üEU2aڂ"üC‚2üͮ7!v))))O m>"R*~R#}=Š!ëͮʂ@҂ î ¸îͮxxGG0O ??! *"1dma: equ cccorg+460h ;set CP/M internal DMA pointer to BASE+80h (tbuff) ; ; The following addresses will depend on the value of RAM if you ; customize CCC.ASM....be sure they correspond to the assembly ; results of CCC.ASM in such cases. If you remove some of the data ; areas from CCC.ASM (in case they aren't needed), be sure to remove ; from here also. ; org ram room: ds 30 ;misc. scratch area (for use by BDS...you can have ;the last ten bytes or so, though, if you really ;need theremaining ; equated values are not to be altered. ; ; ; Special locations in C.CCC containing interesting pointers: ; extrns: equ cccorg+15h ;base of external data area (set by CLINK) cccsiz: equ cccorg+17h ;size of C.CCC for use by CLINK only codend: equ cccorg+19h ;address of byte following last byte of program code ; (set by CLINK) freram: equ cccorg+1bh ;first free address after external area ; (set by CLINK) arg1: equ args ;these are just convenient names for arg2: equ args+for CP/M 2.0) pstrng: equ 9 ;print string (terminated by '$') getlin: equ 10 ;get buffered line from console cstat: equ 11 ;get console status select: equ 14 ;select disk openc: equ 15 ;open a file closec: equ 16 ;close a file delc: equ 19 ;delete a file reads: equ 20 ;read a sector (sequential) writs: equ 21 ;write a sector (sequential) creatc: equ 22 ;make a file renc: equ 23 ;rename file sdma: equ 26 ;set dma readr: equ 33 ;read random sector writr: equ 34 ;write random sector$':}! 2|)*H- ͬ &̓ͅ:d!6K**r|?o&+7!k:[!vHz> R> R m>"RCC2 COM$$$ SUB î ¸îͮxxGG0O ??! *"1$':}! 2|)*H- ͬ: &̓ͅ:d!6K**r|?o&+7!k:[!vHz> R> R m>"RCC2 COM$$$ SUB î ¸îͮxxGG0O ??! *"1$':}! 2|)*H- ͬ: &̓ͅ:d!6K**r|?o&+7!k:[!vHClose error Error on file output ": option error Encountered EOF unexpectedly (check curly-brace balance) Unmatched right brace Undeclared identifier: Illegal external statement Bad declaration syntax Missing legal identifier Function definition not external Need explicit dimension size Too many dimensions Bad dimension value Bad parameter list element Redeclaration of: Missing semicoling from formal parameter list: *!22ͩ(= oo T :” >2͘ Ô k z *2o/ 2o"!/ ͘ ʅ R6 } 2#/ o:ʭ > R> R7|}Mismatched control structure Expecting "while" Illegal break or continue Bad "for" syntax Expecting "{" in switch statement Bad "case" constant Illegal statement Too many cases (200 max per switch) Can't have more than one `default:' :=W| !Hd!"!"$!N"2*+ :! |>*" ) >|>>**r}2r"*DM*e-S `i">|=|:/<|ʆ >=x  J /|>DM"p g ° >%§ >>>z >p >*J  "! $d"  *$+++F+Nq#p#͑ "I2Ks#r#"$*IG! $*"L2&2!'"G!4n_ʃ@͒ b ʗ!@ $}*Gw#w#A xO¹yOã!@ Ñ͑ s#r#:"GyO>y|p : !&4~ 2#" *#"a{ :8!H*7*7H>2~R#H_!$noRn*"! r*"*DM**w#{šzš":!e6C#6C#6I:2\͞ͱ! *w#+6 !~Hòx!9?w#1::*:*À*£!Ã"2 "22\>2*#* DM*~ +zŽ#q#p\2hHdͻ!lë\>2|\2h!lë\!^H\~###~##o&))###DM5:?~# x5ͼX<JGȷl!z*#"ͼچ_:’____**"!$">**"!$">|>>^?0:?A[?_a{? 0!T$*"JI*"!d[*"j:=6667*:G|ژ!:|Hdw #ڷyڜ Ү+~w#>_"))))! ~:2Я2*DM! x7Gon Expecting "{" in struct or union def Illegal structure or union id HarlanE!Expecting "(" Unmatched left parenthesis Sorry; out of memory I'm totally confused. Check your control structure! Out of symbol table space; specify more... Too many functions (63 max)String too long (or missing quote) Attribute mismatch from previous declaration elbowroom unused Error in opening CC2.COM; writing CCI file to disk Include @Illegal "{" encountered externallyDeclaration too complex Miss!  ͼ$  8  * ʅʖʚ u u ʌ ʌ ʌ >2*"2!V  ͼڤ Ò · ͒ ª  ͒ ͒  >^͒  ͒ ! $ 22͒  *$s#r#s#r#"$:<2+*$++++"$:=2:! $>|>| >|>}|͑ k ͑ u *#"+>ʭ ! $> ͒ >|>͑ p ͑ . > >|>>> >͑ >ͦ5!j $_  ¾>|ͅ "I:K>2K! $_:&o&))####ͭ>:&!'xk~# ^*I}|*L" k J   ͅ . ͑  >|>z >k J ͅ #. ##" ! $k ͼ#k J \ >*$+~+ngu *"ͼ\ >*$++*""*DM! xP**ʹ !Hd~a 7`ir**s#r#w#y"*r*"DM**xʩw# Ü"rDM* DM"Ͷ xw+ *+"2!" 2*!lHd~*|})#5>*7! "! 0"#|j+!~ Y>0<:dwG60+Q!H|/g}/o#ɷT/<¢—=/<|2:bkwyx=+ïO2>`% ʩ 4\4\4y(` ʩ+Io&))##ͼG>X*DM_"DM*"Ͷ ͕G>‰:ʭr #`i wy·x+ Õ?*T]~#~"#~#~Gx#~ͼ#~#  +}o "~?W:)r*ѯ*DM! 6# x=~GZѯ<7~#e KywTs w*DM!  x—"!w#}ž Î222222<25!"""! "*+J:*": :!$d!"2!4W:/!=2:<22*DM*##q#p*"ʇ҇*"ҁ>Ç>:¬>|>Çy|: ))))~*w#~w#~G:w#õ: !$*"ʓ::C_:SQZ_Zuy2|:w#:!$w#*DM##": uy|6#:xw#*DM#"q#p#####"*#">}|>|> 2<2 $ր22@+#~56},|,r*+++}|>*+:d*}|>2*"!"+"қ!$>^ҧZ:ʡ!by|†Ã2>2.uLu:>::û:C:>2<2*"*+"##~#fo"þ*+":~~G:##N#F*xy2þ!b$!<É"ʯ~d>2~wþu*:"""*":!Só>2 2"!"*:*"Q:>=2>2>2*"#":2!ͥ":w:w!vʆ!"ͥ:q¡!"2>2!$ogýT]! xDM*":G:!!!!#! !/"/6!0"0ͅ!B!ͼ  +P! >}H!|H!>}|y y/<|> {! ͓!!ͣ!"<"!ʴ!ʴ!͸!ã!!7|!ͅ!7ͅ!ͣ!7ͅ!"!r"!r+","ͅ!7?7ogɯ͜"!"ͧ""k"Ͳ"ʗ"ͺ""ͧ"(#"S"Ͳ"}"H"yʅ"څ"͜"<"ͺ""ͧ"(#"J""*/#"/w*/~+"/*/~"DM*0V+^+"0*0#s#r"0 $2m$á$qy#*%:m$2n$"U$>##2m$á$>w%*B%:m$/>*[%+>2Ü$I[%:n$ [%2n$:m$#b%2m$#á$"k$#&ON%&C[%&%}%&%ʆ%W$"ʝ%<¢%& % %%%>%"%Ý% %&%C&!\͓&*U$6 #:T$<2T$=%6#"k$}$:T$=2T$ &6#S&á$*R$#};&C'%&!:Gz;&!Hd~"R$*P$g&"P$*P$]"P$g&\!̀&̀&R$̀&yʆ&w#†&y::~©&@w#&.µ&&66&&w#&&&> w#&.7ȷ7?:\')'~ )p#)*"u6#)i&*'DMq#p#"*ʹ ҈!a'ͳ.`)#z)j))2'z) |):'ʜ)"ʒ)\o)o):'/2'o)"ʒ)'¬)-p)o))ڼ)J)Ҽ)p)!"8~7"'G)*'  )%))#~#for~# *+ &r }/<| x~#*!""2*+;*V*2*+";*i*2*#";*G {**#"")+ͅ,yڜ*|>}|;*x_*Aڱ*[ڰ*7ñ*xO! + ~#fo~**ͳ.y*O~ <*>*~#*#**! $;*2!"+"222*+"~" "R!,$*"w#####~w!b#**::!"">22:­2!$íí:*"22*"DM""" q#p>2)))) ^#V+:H32:!$ɯ222<2!""*"22͚::ʃ*": :2@!Hd2 º͚"!$͚!4~"$"!8ó:ʻ!bû":ʇG:G:G:*w#:G:w#*s#r#*s#r#*s#r#:X"*#":*|y:y:yDM`i:::¯*DMx!,$ DM*:*"! "2x`iy`iG:BD Software C Compiler v1.42 (part I) What exactly would you like me to compile? Read error Illegal colon Undefined label referenced Syntax error Illegal constant Can't have zeros in strings; to print nulls, use \200 *+ͼ  Ȯ&#<ɯ<#!x`# 2#b#!z`#S#{`#`ir#F#x#A#-#r# Ž#`ir›#x) Ó#­#x|g}o à#¿#|}!+,#xE!+y,##|+###Ͳ###|g}o$|g}o$|g}o#$|-$}-$!|$}$!!/"P$2T$*"k$}$6#6""`#`ir#F#x#A#Cannot open: ͻ$!o$H&d2!"R$*k$> 2m$&7'y % $% $%:%y/$:m$*>/$:=ʜ$2Ü$*%:m$/>*%:ʜ$:<2<@R>:R]$'>.R$'oR %'1'   O\X'7!U Hd#defin#unde#ifde#ifnde#endi#elsWarning: ignoring unknown preprocessor directive String overflow; call BDS &!R0"'!"8"6*J)#(!a'ͳ.ʍ(!h'ͳ.(͚((6*(!{'ͳ.(((!n'ͳ.;(͚((K(!t'ͳ.f(͚((!{'ͳ.(!'ͳ.()L(!'ͳ.x()K(!'H( ('͚(()(i&))*'%(ʺ(>  %(*))"6ͩ( (`i"'"8ʹ (!'Hd<**..... /0/1/U////////͑ }*+*s#r#"#կO2"r+ l+ l++l+wͶ #!4F+!"6*:wrNʹ җ+}|Ý++++>*+}|* " <*>|>õ+Pʹ ++!O+}|\",+ ++,! $\72N-,> T5,> B=,>RE,> FM,> \'{,],:o{,g}|s,{,g}|g}o09?0',*-!, Xʺ,@-,*-ڸ,! $7ɯ3-ڰ, 3-ڸ,))))o,)))o @-, T])))_*-,-ڰ,,O'-,G'-7r`EiH0 ?*- ?*-?*+!"Jo-*t-"7.S-S-!R0"P062J ”-!4ð-¡-!5°-ʰ-ʰ--ڂ- ҽ--կ2̈́.-!%.$!4*P0 -w#-w#*s#r#:."P06y|͑ >}|ð-Duplicate label 2J O.!4<.\.!5<.<.̈́.~.!r $}. q.͝.<.!R0~7ͳ.~#.##Ê.~#foy|>}|~G.ѯ<~#. ÷.x.?..%&! m 1Sw:ĩ2*ͩ2>2;2;2;=2;2;2v;0I#2";!<";*T|g}oR";T";*T";%%C2";";: ʛ6#*s#ráw#w#w#*Ss#r#";!E"; 1!";!;"u2};I#2;2;q1G:};2};xG2};x:;q1#++~2^#V#^#V#~w+p+q+r+s!xd#:*͑z*͢&ͬ&*R*;C2+";!):&0—40×4£#:;2;>͑2;}$*;%Œ!";";!";%";>2;:*z*}$:;+~2G:};:~;&x=%͞%L&e&Ϳ%p͞%:;ȯ2;p%p2;:&p:;C:;4./*;";͗42;:C:;P!)>nc42;>c4k:;w:;!:ʇ:;‘!)/͗4>c4:;ʹM:;\:;:;c4:;c42;{c4zc4*O2~;2;2 <2;q1*;#";!";1/͗42;Ϳ%3 :;<2;>2;:;> 2;: *";> 2;*;|h 5/͗4>`c4>ic4}$:;ʲ ͞%ʓ :~;“ %“ ͘ 2~;#:;ʬ ͞%¬ ͘ 2;%ʸ ,~2*;*;*;*;*;*;:;2;*:*V*:;2s;!)2~;2;2;&:;*;"y;";";";";";";1 },~2#F:s;j z*:;X *;_*;";Ð *;_!)zÌ u /͗4*;!z**ʢ *>c42;#R%F!)/͗4*;";:;¹ *W Ç :; 0͗4Ì |**/͗4z|*Ç e& =,~21F2;:;:& ͞% :;% :~;% p% ,~2q17 ͞,~2ͅ":;K ,~2Fͬ *` *;";F!)z2;F&{ ,~2#q1- ͅ":;B *ʝ !)K *;";ͬ F:;2; :;<2;2~;:}; q1 xG >2}; 2;>2~;}$:; :;+~2:;X2;:;#:};@ @:;>@ :};@>@2;*;";@ :};@2;:;.͗4:};x`x@`’@:;=ʱ@ñ*;";}2;|2;>2;>2;@/͗4>@2;:};`@@:;=@*;";./͗4:};` @`&©ì+-*/+--Įƺ>><<=<><>FOҔCASŘELSœDϕCHAҀBREAːCONTINUőGOTύDEFAULԙBEGIΛENĜIƒINԁMAIΝRETURΎWHILŖSTRUCԋUNIOΌSWITCȗUNSIGNEĂSHORԀREGISTEҞSIZEOƏ^޼||r#:."P06y|͑ >}|ð-Duplicate label 2J O.!4<.\.!5<.<.̈́.~.!r $}. q.͝.<.!R0~7ͳ.~#.##Ê.~#foy|>}|~G.ѯ<~#. ÷.x.?..%&!8G:};2};xG2};x2+&O4"O*;͞͝Kq1";#8́X͝.|:};>2};͑2};ͦ*;͞";G:};2};xͦG2};xͭq1";2;q1#:*͑z*2~;ͳ%Ϳ%%!)/͗4:;R%îR%+>#:*͑z*Ϳ%)͞%):;<2;h#:*͑z*ͬ&*Q*;C2";!):&0—40>2;×4(#:*͑z*͞&ͬ&:;ʞ:;2;:};̄'*¶*;|ʰ!#";!):&=/:;<2;G.͗4:;2;2;:};/͗4:; 2;>2;*u<;2;2;2;!";";q1'C#>͕͑&=:; !) ͇q1 s o q1jo#͕&~:;!) Fq1KA͘q1ɵ:;:2;2:};*;1q1:};2};>͝&>2};2xx:};2};";#2};:&'.'22;q1C*;";,~2";ͩ:#>2~;­2;>2;2~;#^#V#_1";_1";0͗4*;N#F#x–y–F#xʠ~c4È^yc4*;s5#^#V#";>2;2;q1,,~2ͻ:ͅ"";";:;-~2:;@& 2;:;E d /͗4:;& o&)";/͗42 G2 *( :;2;>2~;q1F!)*d :;<2;o&)";/ *;*;*;*;*;:;#q1ʣ :;<2; q1#ʇ + *;";";:;K q1 :};2};̀ q12};:;**=ñ -~2ͻ:+͒1 ###͒1" ͝= = = D1( 2 3 1 A # 72;*;";";";";";";!";*;";_1";*;#:;<2;:;*;*;:*&'&.'z*:;!)";";2;:& :;/̗4/ė4:;=2;#}$Ϳ% :; :;% :;  @:;=( @(*;";C2"; @/͗42;:};`B @ @:;= @:;«%:;دQë%............'//'//'/.'/.//// / ///'/#/'/#/....'//'//'/.'/.////////'/%/'/%/2;2;ͦ*¤*:;*>@*2;ä:;.:;$$#:;2;.:;ʁ)!):;:;$$G:;2;:;)):;}*f!)R:;**!)*>@2;:;*):;**)*>@2;2ͯ'*;";*;";:;}$:;qE%:;q2f;*;V";*;V";:;Qʸø#ʸø7ʸ4ø|k8E¸*;*;";::;?G:;2;:;R:;Rk8ĸ*;C2*;";:;­:[*;";+|08ė4!";2;<2;8>2;͵&R8V¸*;:;G*|g}o.͵&R8¸*;:;<=*)"";Bʸ øMRa6ʸiøl3‘͵&R8¸*;:;o:;g";¶͵&R8:¸*;:;o:;g";=8͵&R8¸*;:;o:;g";:;:;2;:;=8*;|!Hx>c4:;>gc4>Wc42f;:f;:;/̗4/ė4:;ʫ%:;ë%:;ʈ:;^́)H:;q:;ʁ)!):;**!)*>@2;:;:;ʡ:;)):;**)*>@2;;*;";C2";:;2s;**;";";C2";:;2s;~2g;G#^#V# ;"h;^#V#"j;=9*. x \:;ö=X*M x q:;ö=m*:;ö=‚*:;ö!U"h;=£**x ¶b==8**w*h;|U*;|}}U/:s;/͗4*j;͗4:g;Oy'/"'/'/'/ UU-0 UU.0 UU.0 UU-0 U.0U-0 UU30 UU40 UU40 UU30 U40U30 '/'/ UUz0 UU0 UU0 UUz0 U0Uz0 '/'/ . ~. }. |. {. b. e. i. n. t. UUp0 UUu0 UUu0 UUp0 Uu0Up0QG.RO.'/'/UUM0UU>0UU>0UUM0U>0UM0C/F/UUR0UUH0UUH0UUR0UH0UR0'/'/SG.TO.UUW0UU90UU90UUW0U90UW0C/F/UU\0UUC0UUC0UU\0UC0U\01G.2O.1F.1E.1D.1C.2N.2M.2L.2K.1V.1U.1T.2^.2].2\.1UUa02UUa05Ua0!'/"'/)':;22:*#́*u2:};̽I(&ͮ(#:2z***2;>2:};̽''Ϳ(.'#*:;ʫ%*;|:};̄'*V*:*e#~##- ##H͝=8yk:z* #3$Õz*:};•:ʒͽṎ́'2;:Ҳ>2;!ʮ#";+~2:;„':;*;|!#";!):;>2;:;O.4ù /͗4ù *;";*;";:;n!:};O!͕&B!>>c4:;c4.͗4 .͗42; ͕&B!>c4:;c4>{c4.͗4ù ͕&‡!:;`!>}c4.E!:;*C&ž!>&c4c4/E!:;ʲ!!)S :;!**!) :;>!>c4!)>c4>@2; *;";./͗4:;2;";|"}"/͗4%""0͗4>G>)c4*"#I(&:*z*!)q1\".'m,~2#:&i"/͗4('.':*z*!)(~=8#^#V#͗"q1)))T~,,~2G~`2;2;#~2;~?>"=2;#^#V#^#V#";^#V";:;#*;#";R8*;*;C2:;2s;:&¥C&@/͗42;>2;*‹*;";:;:s;>}c4>{c4:;q>c46>c4:;c46!";>2;/:s;̗4/ė4a0͗46C&º:;2s;*‹Løx%%:;q}$*;"y;*;";:;L";%:;q}$*;E%*;|}qq>2;E%*;"y;*;";:;L";*;";*;";*;";>2GV_|+|xn͜0͗4:;2;͜‘2;*͜:;*2;0͗4|)}=ʿʻʷyc4yc4yc4yc4*;|C2*;C2";*;C2|`i";͵&R8@yy2;>2;y>y!9#9#@=8";x2;<)'/'/ 1. 0. /. .. ;. :. 9. 8.6.5.4.3.@.?.>.=.)UU.*UU.-U. '/'/ f0k0 ;. :. 9. 8. 1. 0. /. ..@.?.>.=.6.5.4.3. U. U. UU. UU. U0U0!'/"'/)'/'/)f0k02).)~.)}.)|.*b.*e.*i.)UU0*UU0-U02)UU0*UU0-U0!'/"'/ '/'/ f0k0 UU!0 UU"0 UU"0 UU!0 U"0U!0 UU'0 UU(0 UU(0 UU'0 U(0U'0!/'/)UU0*UU0-U0)'/'/'/'/)UU0*UU0-U0)'/'/)UU0*UU0-U0:&gC&/͗4#c4Å";0͗4?֠!_~ĵ:&ė4@/̗4G.:&ė4=/̗4/͗4>$2;>2;:*>͑z*ʭ:;W!e#2;o!)e#*/͕&l(/͗4|&|:;=2;e##V* z*3$3$ͤ#:;¤**o$#% e##:;*V*:* z*ͦ*Þ:;äǴ)äx**́)*o$#:;?2;% :};>2};&&'.'2};}$:;7 +~2:; !:;¤!:; !):}; ͕&n >6c4:;c4 *;| .͗4 >6c4:;c4>#c4>6c4:;c4 *;";͕& >c4:;c4>sc4>@2; .͗4ù |& :;=2;ͬ&͕& >sc4ù C&!.:}; .͗!:;@#";*;#w";*;~.#9#+~2ͻ:+";*;+";!>";6!>";*;~*;*;s#r#*;s#r#*;s#r#*;s#r#*;s#r#:;w#";#*;*;s#r#*;s#r#*;s#r#*;s#r#*;s#r#:;w#";#|?#} .~28*;+~2;+V+^+";V+^+";V+^+";V+^+";V+^";";*;+~2;+V+^+";V+^+";V+^+";V+^+";V+^";";e#3$ͤ##̓$!";"y;2;<2;2;2;͞%$%$&¶$2;2;$L&$%2;2;Ϳ%e&@%>2;:;!;4ͳ%$*;"y;:; %:; %͞%%:;%!_1%:;'%*;d2_1%1%!_*y;";͞%:;2;e#v$}$#X%͞%n%ͳ%!j%!";Ϳ%ƒ%:;@2;2;e&ʗ%%—%:;=2;!-~2e&ʫ%*;|ʰ%ɯ<͞%:;ë%͞%ʫ%e&ʫ%:; ë%e&ʫ%Ϳ%͞%:;دe#v$%#e&ʫ%ͳ%ʫ%͞%Ϳ%:;͞%ʫ%e&ʫ%%ʫ%L&ʫ%:;e#v$&#&:;-&:;͞%ʫ%:;:;c&<ɯ:;@ë%%Ϳ%ʫ%:;͞%ʫ%Ϳ%ʫ%:;=:;ë%|&:;%&Q,~2+~2&&e#v$&#,~2*u<_1s#r#_1s#r#6#"u<*u<+~'+++V+s5*;";";/͗4*;:;G*;";xf)xH)/͗42;~)xT)W A)/͗4*;zA)xr)̈́'A)x~)*A):;G*;";x)xʯ)/͗4ç)*>@2;)x»)W ä)/͗4*;zä)x)='ç)xʤ)e#v$!)o$#e#v$́)o$#:;G)x)):;Gl)x)r)e#v$*o$#*<~G~#N*> w"<*<~?m*w#> m*O*G:;x*<~+Pʜ*~ʔ*~?@O*~O*~w"<*<~@ë%F>@ʼ*>wx>*>c4>c4>*>*>*>*:;ë%:;ë%BD Sember Bad use of member name Illegal indirection Internal error: garbage in file or bug in C Sorry...ran out of memory. Break it up! Encountered EOF unexpectedly Bad argument list to spare Include @The function "" is too complex; break it up a bit Sub-expression too deeply nested ####88++++88+++++|8z8#####|8z8)8))8)))8))))8)))))8)))))888!888w#w8s#r8s#68s#r828!"8!9DM8!9884n848~4o85n858~5o8^#Vr+s8*#"8^#Vr+s8*+"8^#V88<6# x1*;D1.1#q11~#1^###1";q1X1###71͒1X1Q1###D1?*;#";+_1";͒1:;ʉ1,*;";Ì1-~28~§1*;#";#Ò1¹1#^#V#s5Ò1 1#Ò112v;*;+"w;!";#Ò12v;*w;#";#Ò1*;";>2;22;#622#22+2#62*2#22+2q1X1###62|/g}/o#G[2Ox怱a{ ))) T~#fo|,~2>2;:;:v;ʝ2-ͩ2*w;2*;2ͩ2ͳ2é2_! "l;! 0"n;#|2+!o;~ 2>0<:2w260+2l;ͩ2:c4ä4G8c4C2";.͗4*;";*;";.|«6.͗4E*;6L*;6|6}-ͩ2*t;~6ͳ2~#6-ͩ286ͩ26main*;^s5*u<+&*u<+~"'+V+^s5*u<+'*u<"u<:;:; ]':;*:;@2;:;_!'^#V͗4>2;:;`2;:;:; ʤ':;*:;?2;:;d'?2;////////00 00*u<+~w+++V+^";:;'*;|>(:;(:;_!'~c4/͗4:;':;:(O/:&6([/͗4a/:&6(m/6(*u<+~w+V+^";:;n(*;|>(:;ʁ(:;(:;:;Ÿ(I/:&›(U/͗4g/:&›(s/Û(*u<+~+++++w*u<+~ö(";_1";/͗4*;";s5*;";_1oftware C Compiler v1.42 (part II) Can't open file Write error Missing label Missing semicolon Illegal statement Can't create CRL file Illegal operator Lvalue required Lvalue needed with ++ or -- operator Illegal left operand in assignment expression Mismatched parenthesis Bad expression Bad function name Bad arg to unary operator Expecting ":" Invalid subscript Bad array base Bad structure or union specification Bad type in binary operation Bad structure or union m88#88+8!88{_zW88r+s8888n88*8*8! 8s#r8}8{8|8z8|8|8}8}8z8z8{8{8 8 8 8}888~#fo8!88*8"8 8!!9~#fo8!98&88s#r8s#w8888888888888k8?888)8888888888888888888|g}o8|g}o8|g}o8k88!8}/o,&88!k8)8k8!8|g}o8}/o&8}/o|/g8!8!*3*~#fo#DMC2!S w#z3";6!e6C#6C#6Ie3!S͠3E3̀3w#J3:O |;3e-ͩ28\w32|%+ͩ28\\7+ͩ28\7\2h\2h+ͩ28!e6C#6R#6Lͮ3ͻ3e3*;*;*;6#s#r!<54͋354͋354͋354͋3*;54͋34̀3!f6C#6I:̮3~*;|K4}U4#:4a4>V47i4G:;ʓ4*<~ı**;p#";:;o|ړ4e-@8x͝4:;8­4¹4Q5ä44Q5/5B5͜5ä44OG* }c4|c4ä4/55B5}c4|*;q1=8#^#V";͗"2|;:;.7:;.7<2|;*;j7*;"t;M2w#<7*;*;s#r#";`i)))T~#fo";N#F#xv7y~#v7j7!T";!?";*;zʺ7*;~#­7:;O~?7";Ñ7*;6#x2;!?C2DM?c4 x7*;C2*;j7~8*;M2w#7";í7*;";_1";:;78/͗4:;*/͗4=+8s57-~28q1V8a8=8#^#V#s5F8w8#q1q8#F8g8~8# 8#q1=8#^#V#";/͗4ͩ:8&#q1=8#:*ͳz*=8#I(&F8q18'.'('.'#F8(/9#~=8#^#V#s5&q1q1=8#:*ͳz*=8#I(&F8F8'.'n9#&'F8q1=8#q1R9###:*ͳz*''.'q1=8#9#q1ʵ9&>2};:*͸z*!)&'.':|;ʩ9/͗4õ9:&µ9/͗4*;";/*;|9.͗4ͩ::&:*ͬz*q19,,~2ͻ:ͩ:&'.'#I#q1>2};:*͘z*:&0::;>}-:>{c4~C:#*;#";0:~#=8Fxʕ:.>g";g";og";_1";:&€:/>2;Î::;y/ʎ:/͗4K:#^#V#";/͗4F8q1³:#W+~2#~ʉ1k+@8@8:*;#";X1#ڻ:##û:*;!Ez^#V#~#fo1!\]͓!f6C#6O#6M22J227282;2<2y22A2<26:\2z*"9!"S!"Fr. !"4!F#}~#r! ʘ#~ʘs‰ "4+#~-œ"Q#~ 2g#CHLR+V#2*Q6 #!Ý ~2O2\2i#2 "F |"S*S+"Wd"U"{!6# x/C8 >2|͚ *":_2<*"=!I t.  >2!I>2NFv ͘ ڜ 8 ڊÇ>2y:6 :͗*Us#r:<*=*Us#r"?**Us#r,:$:;*Uft over Linkage complete Bad symbols Executing: Bad option: -Ref table overflowMissing arg to -LSorry; 255 funcs maxs†@*F*U͉*{! "m! 0"o#|+!p~s>0<:wð60+úm. :\<@ >: ]>. > >  s&: A:ô>22A:m!]\͓!e6S#6Y#6My!"*BDMͩ xu 2A:*}ʥ}ʢp#×ͱ [ !Y~#fo͉A! ~#fo&:=2> >2G*B+PY*B+!̀ [ ~*{| }( # 4 >) 7\S v. 7W 2|\!h6k\!h6\!h6k\=7k\ѷk  > > G:A x x _G: x :*p#}) ͱ !" . \ 6 !Y͚ h ͚ h ͚ h ͚ h !e6C#6R#6Lw#m !"͞ [ | >2NF| !"*͸ "~7 ~ ~W#^#)~# a{ !Y~ 7@ ~#fo"# ~#fo" !"~s#  É \#~+::zB ~ @##~@ B d ~X X #d "4 6~~ s~ .~ #d x> ~ :y \ .  . !6x  !+~27!\\͓>28+ \ !"͞ ~ #8 > :́!i\͓6 *U͚ ih ]*U^#V*U"{[ ͊!\MÓ!M\Ó ~#•:<2°i *DM**{:o| \yo&"͚ *{"}+|*} ;#:DM*;!Lz;~#%;~,;###;og}|";*;;*;}c4|c4)?| c4+Z;;_1";: ʒͽṎ́'2;:Ҳ>2;!ʮ#";+~2:;„':;*;|!#";!):;>2;:;O.:&ė4@/.'#I#q1>2};:*͘z*:&0::;>}-:>{c4~C:#*;#";0:~#=8Fxʕ:.>g";g";og";_1";:&€:/>2;Î::;y/ʎ:/͗4K:#^#V#";/͗4F8q1³:#W+~2#~ʉ1k+@8@8:*;#";X1#ڻ:##û:*;!Ez^#V#~#fo6*#:Bs#r#6$*9*U#s#r . **{|?o&+͠. ͏ COM BD Software C Linker v1.42 Error reading Dir full; Error writing Can't close No main function in Missing function(s): Type the name of a CRL file to search; to search DEFF, ^Q- to abort: MAIDEFF CRLDEFF2 CRLC CCC Can't find Last code address: Externals start at occupy bytes, last byte at Top of memory: Stack space: Out of memoryK le! "D'!Y"D'xy|}*D N#F*D^#Vp+qDMq#p! ~[`i"Bd!J}|~#l~W#^#P! ^#V! N#F͘ g 7? Ø . ͗+A:. *?I. *A. +z#A. *9+A:!. A ~# )x> Q> Q>, |Y}Yb0:  !]\͓w ̈́ 6 w#…:J(. :J=*K!6 # "w#ðx2!~#*U*{DM!w#z{p*U ͱ ## :6+ *4~s # < 26:y:7 :8 Ô - #~ 2gSʈ Tʳ O Wʎ E! Y2 Dʔ s  6. # 2A 2A >2J# " 2J#"K~#"© B # "9>2;  W. ~ # ))))_ 0 ??#]:z:]2z 2z # "=>2< # !e6S#6Y#6M 6 c !"*{i V "{[ *4 ښ ͽ ͉"{͠ !%J@>2N !% ښ w# ڷ © +~w!))))_ s 7?*} ͚ ~#} ~#"͚ !~#"`i)|! w2x! [ w6 :2|͞*{~#M##"M:G~} `i"2N _#N#F#*{ N#F#͗"x^#V#*{^#V*r+s à"{|/g}/o#DM*}z{!~#~G#N#j&)Y^#V͉"Ox#n&)Y^#V~##6 O! #*Os#r!2~t@ʉ:<2͸ Ycw#t6#6#6++:~#:o&)Y~#fo͉"O*M:6^+Vr#s#*s#r+~w!Y *{s#r*|}##"*WDM  #DM*W><)~+ x+*W~=D ~cGc_Y#C#ŷ Gx F#*{ N#F#͗"x^#V#*{^#V*r+s à"{|/g}/o#DM*}z{!~#~G#N#j&)Y^#V͉"Ox#n&)Y^#V~##6 O! #*Os#r!2~t@ʉ:<2͸ Ycw#t6#6#6++:~#:o&)Y~#fo͉"O*M:6^+Vr#s#*s#r+~w!Y *{s#r*|}##"1K͞+ '+FÎr ͖ 8{ Ăy0w#w#w^#V#*~#fo^#*~#fo^#V#*n^#*n^#V# ~#fo^#& ~#fo!+!#!+!#!+!+}|z{|}|z7||7zZZ)|/g}/o#|͉k|/g}/o#ɯ2qZZk:q|/g}/o#|/g}/o#:q<2qqDM!xxGyO҃)v|͔`i|)Öxڷz/W{/_ѯzW{_=yOxGæ2qZZ͉M|}ȯ|g}o)|/g}/o!a{   `OE!y6$ -7rBo&))T])))!y"!@"!" ʞ!F#x±~#±!b2r~# "2r+}|~#G:rx"2r+w# +6#!6#2w2x*s!>r<o&F=-` r'~h6!+`W?_!~7z?` :>ª@w#.¶ww#E*>?w#> w#.7:771d.͇ !d.͞ *ͨ 8͇ 2 21d.͡!,2:OʡFExAʂD,RʥCdQ"MXT5Lʏa͇ OFEADRCQMTL?Hcopyright (c) 1980by Leor Zolman 'd ! ^#V#*0}o|g*  ":D x=D yBD Software C Librarian v1.42 Function buffer size = bytes.That command is not recognized C Librarian commands: (note: "file#" is a single digit, 0-9) o[pen] file# [d:]filename f[iles] e[xtract] file# funcname a[ppend] file# [funcname] d[ese Not enough room left in CRL file directory Not enough room in file (64K max) Function buffer too small Out of disk space That command requires a file# Second file# required Too many file#'s given That command requires a function name argument 'Rename' requires a second function name argument Too many names given That file# is not currently assigned to any file (Quit) Are you sure (y/n)? Aborting all operations on .CRL There aren't any files open right now Please don't begin f#z/W{/_!9~#fo! ! ! ! ! ! !9~#A"s!`*"!"!Y">2>2>22!"!"!@"!" ʞ!F#x±~#±!b2r~# "2r+}|~#G:rx"2r+w# +6#!6#2w2x*s!>r<o&F=-` r'~h6!+`W?_!~7z?` :>ª@w#.¶ww#E*>?w#> w#.7:77lete] file# funcname r[ename] file# funcname_old funcname_new c[lose] file# q[uit] [file#] m[ake] filename t[ransfer] source_file# dest_file# funcname h[elp] l[ist] file# File not found Error in closing file Error in reading file No more directory space file # size= bytes; dir space= /512 bytesFunction not found Function already exists Function buffer empty Error in writing file No functionsCRL file already existsSorry; only : files (0-9) allowedThat file# is already in uilenames with numbers! I confuse easily. A write error occurred while closing. This is bad news; the original CRL file is intact (unless you have just created it from scratch) but it may contain extra invisible functions if you have transferred any into it before closing. To get rid of these possible ghosts, open the file again (after clearing some disk space!) and then close it immediately. ͇ ,**7##"~#> !"##ö~#5 ~͘ *I ?9 G5 0 #~ͯ , *5 # 70 #~ͯ , 5 #~ͯ , , 5 Q ͘ 0:?A_[?0_v Ç > v > v |/g}/o#|¹ } ҹ 0v ! ͨ !  ͨ `i| ) x z/W{/_ѯzW{_=yOxG z{|} / |/g}/o#5 DM!xxGyOG ): ~b <@v >:v #~ } v # o ͇ ͘ *?_A[?a{ 0 ?0:? ~ #õ  :_!6 ͑ !͵ o&))))).go&Ve !~#"** ! 2͇ 7s :*:2*"e "L 2T 2́yo&"*d.y +|c :W *w#"} W !"~ #Ò ###’ ++**~µ 7 ~# ##í ͘ G~͘ ~ ͯ # ~ͯ # N#F# ^#Vͦ ?͇ ,ͦ T͇ ,~##!!*d*W ,W ,W ,W ,! 6! ~ͦ 6ͅ ! ~5ͦ ! 5ͅ ! 6͑ 2>*v ͽ ͘ 2{@͇ >??H ͵ ͨ :FHMQ ~Y 2<2#|͇ ,*~ͯ *#*"*"Ͳ*"ͲND~w*!\? J \}͇ , !\ͅ !6#6#6\& ڿ %–ͦ ͞ }*͞ E*ͯ ͘ w#+~wɷ/<_"|G͞ $$DM*~# x o& ~+ z'*"*"Ͳ*"ͲWM*~x**"**"::2͂2"":M >:v ͑ : ~²͇  ##>Mv >Av >Iv >Nv v ~#>:v ^#V#> v ~#~#fo;; ;; CMAC.LIB by Robert T. Schumaker and David A. Nichols ;; February 25th 1980 ;; ;; Modified by Leo Kenen to accept multiple functions ;; per source file ;; July 10th 1980 ;; ;; "EXDWREL" and some documentation added by Leor Zolman ;; July 12th, Aug 12th 1980 ;; ;; This macro package, for use with Digital Research's MAC macro ;; assembler, does a real nifty job of automating the transformation ;; of customized assembly language functions into the CRL function ;; format. the ;; libraries have been once more reorganized; all the C functions in ;; STDLIB1.C and STDLIB2.C are now compiled into DEFF.CRL, and all the ;; assembly functions are assembled into DEFF2.CRL (from DEFF2.ASM). ;; The general procedure for making an .ASM file into a .CRL file ;; (assuming the .ASM file utilizes the macro commands defined here) is: ;; ;; 1) assemble the file, with: A>mac name.asm $whatever... ;; ;; 2) load the file, with: A>load name ;; ;; 3) rename the file, with: A>re~~ 7 # ͇ ,͡ G:M&  > ک:OD x D S #͵ x6:^ ͘ @w#͘ .w ͯ w w# a  6C#6R#6L͘! 6͇ ,͇͘ ,͘͘\͇ , ͇ ,ѷ  :0 % :0 r( ͇ >27!V 6#7 6#6 #D ###6#6#6* * `i)|! w2x! ͦ wͅ ! :w`i͞ "d.|* V͇ ,*s yo&"e d.* +| <2*} ~#"= " "~ͨ ':T~Y :T5 ~ 2 " "~ͨ Y ͅ w:FLCQA " ͵ ͅ Ғ:R ":R< : ʵ͇ ,**? J *ͅ e : -e -e -e -6ɯ2x %x :͑ ͇ 2͇ xt > v >(v xM >)v x ͒ ~#fo+͇ ͨ  ͇ ͞ ͨ 4͇  ͦ :’n͇ ,"#N#F`i"++*~͘ #ͯ ¬++~w#q#p#6#* }|:2*"*" **6#s#r,|g##͞ ͨ > v v ͑ ~¥:B͇ ͘ ͑ Y,: TS 6͇ M ͑ !\"? !,": !\ͩ6L+6R+6BͿ 6$#6$#6$Ϳ ͅ & ,%¨*"!,"!"*~; ^#V#" ~#fo͞ *"*w#N#F#" ^#Vͦ *͞ s#r#"BK \Ϳ +͇ ,#^#V*͞ *6#s#r5!\ͦ : 6*ͦ 2*ͷ!e6B\!e6C!u6B\!e6$#6$#6$!u6C\ w#¬+]m> #~#=2Just begin your assembly file with the statments ;; ;; maclib bds ;get system info and run-time constants ;; maclib cmac ;include this macro library ;; ;; and stick in the required pseudo-ops for creating the CRL directory ;; and handling function identification and relocation. The file ;; DEFF2.ASM, included with the BDS C package, contains ALL the library ;; functions which are coded in assembly language. It is now painless ;; to customize the library routines and/or add your own. Note that n name.crl=name.com ;; ;; and that's all there is to it. Be careful when using the pseudo-ops, ;; though; the error detection is not too fabulous. Some possible hassles ;; to watch out for: ;; ;; 0) remember to use the "maclib" statement to read in the ;; macro definitions in this file and the global equates ;; in BDS.LIB ;; ;; 1) DO NOT have "end" statements anywhere except at the end of ;; the entire source file ;; ;; 2) Don't use labels with identical names in separate functions. ;; ;; 3) Remember to use the "reloc" pseudo-op for all local address ;; references (i.e., `jmp's to other labels within a function), ;; and the "exrel" pesudo-op ONLY to call a function defined ;; as a "needed function" by way of being placed in the arg ;; list to "prelude". For dw ops, use "dwrel" for local ;; labels and "exdwrel" for needed-function addresses ;; ;; 4) Make sure that the order in which the functions appear is ;; EXACTLY the same as the order in which only thing necessary at the ;; start of a new function, but it must include both the name ;; of the function itself AND the names of all needed func- ;; tions. ;; ;; 3) The "reloc" and "dwrel" ops must NOT be used when ;; referencing one of the needed functions appearing in ;; the "prelude" op of a given function; for this special ;; purpose, the "exrel" and "dwexrel" ops must instead be used. ;; ;; The Macros: ;; ;; DIRECT ;Indicates start of directory. ;; ; Put PRELUDE function, ;; ;Indicates start of code for a given function ;; ;This macro also creates a table of all the ;; ;needed functions which are external to ;; ;this function. Any refrences to these needed ;; ;functions must be relocated using the ;; ;EXREL macro. Note that if you need to specify ;; ;more than one needed function, then the list ;; ;must be enclosed in angle brackets. ;; ;; POSTLUDE function_name ;; ;Indicates end of code ; ;; ; LXI H,BAZ ;; ;becomes ;; ; RELOC ,BAZ ;; ; ;; ;if BAZ is a symbol local to the function. ;; ; The general rule of thumb here is that ;; ;both "exrel" and "reloc" take two arguments, ;; ;and the assembler normally assumes that spaces ;; ;separate the two arguments; thus, op codes ;; ;that include spaces must be entirely enclosed ;; ;in angle brackets. RELOC CANNOT BE USED ON ;; ;"DW" ops...FOR THAT, USE "DWREL" ;; ;; EXREL OP,LABEL se EXDWREL ;; ;; EXDWREL ADDR ;DWREL's counterpart for needed function ;; ;references ;; ;; Kludge Notes: ;; The DIRECT and ENDDIR macros can probably be ;; flushed if someone is willing to spend the time ;; fixing the package. (-Leo) ;; ;; Grateful Notes: ;; This package may have saved my sanity. Thank you, ;; Tennessee Tech Microcomputer Association, for ;; hacking it up. And thanks, Leo, for making it ;; even better. (-leor) ;; ;; ; BDS C Macro Library (CMAC.LIB) v1.4 their "define" ;; ops appear in the directory section. ;; ;; Differences between this version of CMAC.LIB (v1.4) and the last (and ;; only prior) version (1.32): ;; ;; 1) The "direct" and "enddir" ops have been added, and all of ;; the "define" ops must now be placed at the start of the ;; source file (between "direct" and "enddir"). The "define" ;; op must NOT be placed at the start of the actual function; ;; only "prelude" goes there. ;; ;; 2) The "prelude" op is now the this right after the ;; ; "maclib" statments. ;; ;; DEFINE function_name ;; ;Creates directory entry for function. ;; ; Have one of these for each function you ;; ; include later, and keep the order the same. ;; ;; ENDDIR ;; ;Indicates end of directory. ;; ; After this pseudo-op, place ;; ; each of your functions-- ;; ; IN THE SAME ORDER BY WHICH ;; ; YOU ENTERED THEM IN THE ;; ; DIRECTORY SECTION. ;; ;; PRELUDE function_name,needed_func_name ;; or for a given function ;; ; Note that the name MUST be specified. ;; ;; RELOC OP,ADDR ;; ;Relocates given operation for the LOCAL ;; ;address given (i.e., "ADDR" is a location ;; ;within the function body, but use "exrel" for ;; ;references to the "needed" functions). ;; ; For operations which have more than one ;; ;operand (i.e., spaces embedded ;; ;in the opcode of the instruction),it is ;; ;necessary to specify the macro ;; ;call in a very kludgy way. E.g., ;; ;; ;Like RELOC, but used to relocate ;; ;references to the functions ;; ;which were specified in the ;; ;PRELUDE to the function. These ;; ;are functions external to the ;; ;routine which is currently ;; ;being defined. Angle brackets must again ;; ;be used for OPs containing embedded spaces. ;; ;; DWREL ADDR ;Used as a relocatable "dw" pseudo-op, since ;; ;using a "reloc" only works with an op code ;; ;type instruction. For local addresses only, ;; ;otherwise u; by Robert T. Schumaker and David A. Nichols ; Modified by Leo Kenen ; ; ;; @functs set 0 ;; number of functions processed @c set 0 ;; initialize count @there set 0305h ;; after two pages and 5 reserved bytes ; @genpos macro @addr ;; force the address to be offset from zero @where set @addr-@there-@offset ;; instead of where MAC thinks it is endm ; @genlab macro @num,@foo ;; generate a label for the relocation table @genpos $+1 ;; to point to the byte following the @op @r&@foo&@f&@num: equ @where endm ; @gendw macro @num,@foo ;; generate a relocation table entry dw @r&@foo&@f&@num endm ; @gendb macro @name @last set 0 ;; this macro generates a table entry for irpc c,<&@name> ;; @name, and sets bit 7 on the last character @last set @last+1 endm @count set 0 irpc c,<&@name> @count set @count+1 @temp1 set @count eq @last @temp2 set not @temp1 if @temp1 db '&C' or 80h endif if @temp2 db '&C' endif endm endm ; @genjmp macro @label,@num ;; set up tthat we can @genlab %@c,%@functs ;; generate a label at address for the postlude @genpos @addr ;; macro and put it in the relocation table &@op @where ;; force to be orged at 'start' of program endm ;; ;; Relocate function involving an external needed function: exrel macro @op,@addr @exrel @op,@addr,%@functs endm ;; @exrel macro @op,@addr,@num reloc @op,@l&@addr&@x&@num endm ;; ;; Relocate a "dw" involving an external needed function: ; exdwrel macro @addr @exdwrel @addr,%@f equ $-0100h ;; jump table so that we can get @functs set @functs+1 ;; We are processing another function irp pp, if nul &PP exitm endif @gendb pp endm db 00 dw @name&@end-$-2 ;; this is how long the routine is. the ; ;; -2 is to get rid of the dw itself. ; ;; @offset set $-0100h-@name&@beg ;; the routine proper starts here at ; ;; the beginning of the jump table ; ;; all the different offsets are needed irp pp, ;; to fool both MAC and the C generated by reloc dw @num ;; tell how many @b set 0 rept @c @b set @b+1 ;; bump @counters @c set @c-1 @gendw %@b,%@functs endm @final set $ ;; next free byte in file endm ; acro is set up so that the human ;; doesn't have to do anything special @postlude @name,%@c ;; since we supply the parameter pass endm ; @postlude macro @name,@num ;; sets up a table of relocation point @name&@end: equ $ ;; using the labels ; ; CCC.ASM (C.CCC) v1.42 ; ; This is the BDS C run-time package. Normally, it resides at ; the start of the TPA (at address BASE+100h, where BASE is either ; 0000h or 4200h depending on CP/M implementation.) The code ; generated by the compiler ALWAYS sits immediately after the end of ; this run-time package code. ; ; Equate statements in CAPITAL letters may be customized by the ; user in order to change a) the origin of the run-time package, ; and b) the origin of the run-he jump table at the beginning of @l&@label&@x&@num: jmp 0000h ;; the program endm ; @dwlab macro @num,@foo ;; this is needed to generate a relocation @genpos $ ;; label for the 'dwrel' macro @r&@foo&@f&@num: equ @where endm ; dwrel macro @addr ;; need to be able to relocate dw @c set @c+1 ;; and get correct relocation @dwlab %@c,%@functs ;; parameters @genpos @addr dw @where endm ; reloc macro @op,@addr ;; relocate the opcodes parameter ; @c set @c+1 ;; count this location so uncts endm ; @exdwrel macro @addr,@num dwrel @l&@addr&@x&@num endm ; ; direct macro @functs set 0 org 0100H endm enddir macro db 80H dw @final-0100H org 0305H ;; This should be generalized endm ; define macro @name ; ;; to be able to use LOAD if nul @name db 9Dh ;; make main else @gendb @name ;; point to module endif dw @name&@beg ;; start of block endm ; prelude macro @name,arglist ;; set up a list of needed routines @there set $ @name&@beglinker. if not nul &PP reloc jmp,@name&@strt exitm endif endm irp pp, if nul &PP exitm endif @genjmp pp,%@functs endm @name&@strt: equ $ ;; end of the jump table endm ; postlude macro @name ;; this macro is set up so that the human ;; doesn't have to do anything special @postlude @name,%@c ;; since we supply the parameter pass endm ; @postlude macro @name,@num ;; sets up a table of relocation point @name&@end: equ $ ;; using the labels time RAM area. If you will be ; generating code to run in a non-CP/M environment, set the CPM ; equate to zero and make sure to set the ORIGIN, RAM and ; EXITAD equates to fit your custom run-time configuration. ; ; The "lxi sp,0" instruction at the start is replaced by the sequence: ; ; lhld base+6 ; sphl ; ; by CLINK at link time, unless the -t option is used with CLINK, ; in which case the "lxi sp" remains there and the value used to ; initialize the SP is the argument given to the "-t" option. ; page 76 title 'BDS C Run-Time Module (c.ccc) v1.42 11/22/80' CPM: EQU 1 ;True if to be run under CP/M DMAVIO: EQU 1 ;True if using DMA video library routines and ;need parameters initialized if cpm base: equ 0 ;start of ram in system (either 0 or 4200h for CP/M) bdos: equ base+5 ;rest of these used by CP/M-based configurations. tpa: equ base+100h nfcbs: equ 8 ;maximum # of files open at one time tbuff: equ base+80h origin: equ tpa exitad: equ base start of the ; "init" routine!!!!!!!! ; org origin lxi sp,0 ;this is changed by CLINK to lhld base+5h nop ;this first is usually turned into sphl by CLINK nop! nop ;Simple initialization or patches may be nop! nop! nop ;inserted here, but better to do all that nop! nop! nop ;in the "init" routine call init ;do ARGC & ARGV processing, plus misc. initializations call main ;go crunch!!!! jmp vexit ;close open files and reboot extrns: ds 2 ;set by CLINK to external data base addrmp vfgfcb ;compute address of internal fcb for fd in A ENDIF IF NOT CPM ;if not under CP/M, file I/O routines close: jmp verror ;are not used. setfcb: jmp verror fgfd: jmp verror fgfcb: jmp verror ENDIF ds 22 ;reserved setfcb3: mov m,a ;this is a patch from the "vsetfcb" routine, inx h ;which causes the random record bytes of the mov m,a ;fcb being initialized to be zeroed. (Former inx h ;versions had a "ds 30" above, so this keeps mov m,a ;all the addresses consistent betwdw offset_from_extrns ; >= 256 ; ldei: pop h ;get address of offset mov e,m ;put offset in DE inx h mov d,m inx h push h ;save return address lhld extrns ;add offset to external area base dad d mov a,m ;and get the value into HL inx h mov h,m mov l,a ret ; ; short-displacement, double-byte external indirection: ; ; format: call sdei ; get 16-bit value in L ; db offset_from_extrns ; < 256 ; sdei: pop h mov e,m inx h push h mvi d,0 lhld extrns dad dad d mov l,m ret ; ; long-displacement, double-byte local indirection: ; ; format: call ldli ; get 16-bit value in HL ; dw offset_from_BC ; >= 256 ; ldli: pop h mov e,m inx h mov d,m inx h push h xchg dad b mov a,m inx h mov h,m mov l,a ret ; ; short-displacement, double-byte local indirection: ; ; format: call sdli ; get 16-bit value in HL ; db offset_from_BC ; < 256 ; sdli: pop h mov e,m inx h push h xchg mvi h,0 dad b mov a,m inx ;re-boot location endif if not cpm ;fill in the appropriate values... ORIGIN: EQU NEWBASE ;Address at which programs are to run RAM: EQU WHATEVER ;Read-write memory area for non-CP/M configurations ; (defaults to immediately after C.CCC under CP/M) EXITAD: EQU WHENDONE ;where to go when done executing endif ; ; The location of the jump vectors and utility routines must remain ; constant relative to the beginning of this run-time module. ; ; Do NOT change ANYTHING between here and theess cccsiz: dw main-origin ;size of this code (for use by CLINK) codend: ds 2 ;set by CLINK to (last addr of code + 1) freram: ds 2 ;set by CLINK to (last addr of externals + 1) ; ; Jump vectors to some file i/o utility routines: ; error: jmp verror ;loads -1 into HL and returns exit: jmp vexit ;close all open files and reboot IF CPM close: jmp vclose ;close a file setfcb: jmp vsetfcb ;set up fcb at HL given filename at DE fgfd: jmp vfgfd ;return C set if file fd in A not open fgfcb: jeen this pop d ;and earlier 1.4's) pop b ret ; ; The following routines fetch a variable value from either ; the local stack frame or the external area, given the relative ; offset of the datum required immediately following the call; ; for the "long displacement" routines, the offset must be 16 bits, ; for the "short displacement" routines, the offset must be 8 bits. ; ; ; long-displacement, double-byte external indirection: ; ; format: call ldei ; get 16-bit value in HL ; d mov a,m inx h mov h,m mov l,a ret ; ; long-displacement, single-byte external indirection: ; ; format: call lsei ; get 8-bit value in L ; dw offset_from_extrns ; >= 256 ; lsei: pop h mov e,m inx h mov d,m inx h push h lhld extrns dad d mov l,m ret ; ; short-displacement, single-byte external indirection: ; ; format: call ssei ; get 8-bit value in L ; db offset_from_externs ; < 256 ; ssei: pop h mov e,m inx h push h mvi d,0 lhld extrns h mov h,m mov l,a ret ; ; Flag conversion routines: ; pzinh: lxi h,1 ;return HL = true if Z set rz dcx h ret pnzinh: lxi h,0 ;return HL = false if Z set rz inx h ret pcinh: lxi h,1 ;return HL = true if C set rc dcx h ret pncinh: lxi h,0 ;return HL = false if C set rc inx h ret ppinh: lxi h,1 ;return HL = true if P (plus) flag set rp dcx h ret pminh: lxi h,1 ;return HL = true if M (minus) flag set rm dcx h ret pzind: lxi d,1 ;return DE = true if Z set rz dcx d ret pnzind: lxi d,0 ;return DE = false if Z set rz inx d ret pcind: lxi d,1 ;return DE = true if C set rc dcx d ret pncind: lxi d,0 ;return DE = false if C set rc inx d ret ppind: lxi d,1 ;return DE = true if P (plus) flag set rp dcx d ret pmind: lxi d,1 ;return DE = true if M (minus) flag set rm dcx d ret ; ; Relational operator routines: take args in DE and HL, ; and return a flag bit either set or reset. ; ; ==,th E cmp e ret blas: xchg ;return C if HL < DE, signed albs: mov a,h ;return C if DE < HL, signed xra d jp albu ;if same sign, do unsigned compare mov a,d ora a rp ;else return NC if DE is positive and HL is negative stc ;else set carry, since DE is negative and HL is pos. ret bgas: xchg ;return C if HL > DE, signed agbs: mov a,h ;return C if DE > HL, signed xra d jp agbu ;if same sign, go do unsigned compare mov a,h ora a rp ;else return NC is HL is positive andntain address compatibility with some ; pre-release v1.4's. usmod: mov a,h ;unsigned MOD: return (DE % HL) in HL ora l rz push d push h call usdiv pop d call usmul mov a,h cma mov h,a mov a,l cma mov l,a inx h pop d dad d ret smul: xra a ;signed multiply: return (DE * HL) in HL sta tmp call tstn xchg call tstn call usmul smul2: lda tmp rar rnc mov a,h cma mov h,a mov a,l cma mov l,a inx h ret tstn: mov a,h ora a rp cma mov a,h ora a jm usd3 dad h inr b jmp usd2 usd3: xchg usd4: mov a,b lxi b,0 usd5: push psw usd6: call cmphd jc usd7 inx b push d mov a,d cma mov d,a mov a,e cma mov e,a inx d dad d pop d usd7: xra a mov a,d rar mov d,a mov a,e rar mov e,a pop psw dcr a rz push psw mov a,c ral mov c,a mov a,b ral mov b,a jmp usd6 sdiv: xra a ;signed divide: return (DE / HL) in HL sta tmp call tstn xchg call tstn xchg call usdiv Routines to 2's complement HL and DE: ; cmh: mov a,h cma mov h,a mov a,l cma mov l,a inx h ret cmd: mov a,d cma mov d,a mov a,e cma mov e,a inx d ret ; ; The following routines yank a formal parameter value off the stack ; and place it in both HL and A (low byte), assuming the caller ; hasn't done anything to its stack pointer since IT was called. ; ; The mnemonics are "Move Arg #n To HL", ; where arg #1 is the third thing on the stack (where the first ; an >, < : ; eqwel: mov a,l ;return Z if HL == DE, else NZ cmp e rnz ;if L <> E, then HL <> DE mov a,h ;else HL == DE only if H == D cmp d ret blau: xchg ;return C if HL < DE, unsigned albu: mov a,d ;return C if DE < HL, unsigned cmp h rnz ;if D <> H, C is set correctly mov a,e ;else compare E with L cmp l ret bgau: xchg ;return C if HL > DE, unsigned agbu: mov a,h ;return C if DE > HL, unsigned cmp d rnz ;if H <> D, C is set correctly mov a,l ;else compare L wi DE is negative stc ret ;else return C, since HL is neg and DE is pos ; ; Multiplicative operators: *, /, and %: ; smod: mov a,d ;signed MOD routine: return (DE % HL) in HL push psw ;save high bit of DE as sign of result call tstn ;get absolute value of args xchg call tstn xchg call usmod ;do unsigned mod pop psw ;was DE negative? ora a ;if not, rp ; all done mov a,h ;else make result negative cma mov h,a mov a,l cma mov l,a inx h ret nop ! nop ;maimov h,a mov a,l cma mov l,a inx h lda tmp inr a sta tmp ret usmul: push b ;unsigned multiply: return (DE * HL) in HL call usm2 pop b ret usm2: mov b,h mov c,l lxi h,0 usm3: mov a,b ora c rz mov a,b rar mov b,a mov a,c rar mov c,a jnc usm4 dad d usm4: xchg dad h xchg jmp usm3 usdiv: mov a,h ;unsigned divide: return (DE / HL) in HL ora l ;return 0 if HL is 0 rz push b call usd1 mov h,b mov l,c pop b ret usd1: mvi b,1 usd2:jmp smul2 cmphd: mov a,h ;this returns C if HL < DE cmp d ; (unsigned compare only used rc ; within C.CCC, not from C) rnz mov a,l cmp e ret ; ; Shift operators << and >>: ; sderbl: xchg ;shift DE right by L bits shlrbe: inr e ;shift HL right by E bits shrbe2: dcr e rz xra a mov a,h rar mov h,a mov a,l rar mov l,a jmp shrbe2 sdelbl: xchg ;shift DE left by L bits shllbe: inr e ;shift HL left by E bits shlbe2: dcr e rz dad h jmp shlbe2 ; ; d second things are, respectively, the return address of the ; routine making the call to here, and the previous return ; address to the routine which actually pushed the args on the ; stack.) Thus, a call to "ma1toh" would return with the first ; passed parameter in HL and A; "ma2toh" would return the second, ; etc. Note that if the caller has pushed [n] items on the stack ; before calling "ma [x] toh", then the [x-n]th formal parameter ; value will be returned, not the [x]th. ; ma1toh: lxi h,4 ;get first arg ma0toh: dad sp mov a,m inx h mov h,m mov l,a ret ma2toh: lxi h,6 ;get 2nd arg jmp ma0toh ma3toh: lxi h,8 ;get 3rd arg jmp ma0toh ma4toh: lxi h,10 ;get 4th arg jmp ma0toh ma5toh: lxi h,12 ;get 5th arg jmp ma0toh ma6toh: lxi h,14 ;get 6th arg jmp ma0toh ma7toh: lxi h,16 ;get 7th arg jmp ma0toh ; ; This routine takes the first 7 args on the stack ; and places them contiguously at the "args" ram area. ; This allows a library routine to make one ca pop b ;restore BC ret ; ; UP TO THIS POINT, ABSOLUTELY NO CHANGES SHOULD EVER BE MADE ; TO THIS SOURCE FILE (except for customizing the EQU statements ; at the beginning of the file). ; ; ; This routine is called first to do argc & argv processing (if ; running under CP/M) and some odds and ends initializations: ; init: pop h ;store return address shld tmp2 ; somewhere safe for the time being IF CPM lxi h,arglst-2 ;set the "argv" that the C main program ENDIF IF NOT mvi a,0dbh ;"in" op, for "in xx; ret" subroutine sta iohack mvi a,0d3h ;"out" op for "out xx; ret" subroutine sta iohack+3 mvi a,0c9h ;"ret" for above sobroutines sta iohack+2 ;the port number is filled in by the sta iohack+5 ;"inp" and "outp" library routines. ;Initialize DMA video parameters: IF DMAVIO ;if we're using DMA video routines, lxi h,0cc00h ;set up default values (may be changed shld pbase ;to whatever suits). Video board address, lxi h,16 shld xsize ;# of lines, l mov a,b ora a ;if no arguments, don't parse for argv jnz initl lxi d,1 ;set argc to 1 in such a case. jmp i5 initl: mov a,m ;ok, there are arguments. parse... stax d ;first copy command line to comlin inx h inx d dcr b jnz initl xra a ;place zero following line stax d lxi h,comlin ;now compute pointers to each arg lxi d,1 ;arg count lxi b,arglst ;where pointers will all go xra a ;clear "in a string" flag sta tmp1 i2: mov a,m ;between args... inx h cpi ' ' cpi '"' ;we are in a string. jnz i3 ;check for terminating quote xra a ;if found, reset "in string" flag sta tmp1 dcx h mov m,a ;and stick a zero byte after the string inx h ;and go on to next arg i3a: cpi ' ' ;now find the space between args jnz i3 dcx h ;found it. stick in a zero byte mvi m,0 inx h jmp i2 ;and go on to next arg i5: push d ;all done finding args. Set argc. mvi b,nfcbs ;now initialize all the file info lxi h,fdt ;(just zero the fd table) i6: mvi m,ll to arghak ; and henceforth have all it's args available directly ; through lhld's instead of having to hack the stack as it ; grows and shrinks. Note that arghak should be called as the ; VERY FIRST THING a function does, before even pushing BC. ; arghak: lxi d,args ;destination for block move in DE lxi h,4 ;pass over two return address dad sp ;source for block move in HL push b ;save BC mvi b,14 ;countdown in B arghk2: mov a,m ;copy loop stax d inx h inx d dcr b jnz arghk2CPM lxi h,0 ENDIF push h ; will get. ;Initialize storage allocation pointers: lhld freram ;get address after end of externals shld allocp ;store at allocation pointer (for "sbrk.") lxi h,1000 ;default safety space between stack and shld alocmx ; highest allocatable address in memory ; (for use by "sbrk".). ;Initialize random seed: lxi h,59dch ;let's stick something wierd into the shld rseed ;first 16 bits of the random-number seed ;Initialize I/O hack locations: xi h,64 shld ysize ;# of columns, lxi h,1024 shld psize ;and total # of characters on screen ENDIF IF CPM ;under CP/M: clear console, process ARGC & ARGV: mvi c,11 ;first we clear console input if there call bdos ; happens to be a stray character there... ani 1 jz initzz mvi c,1 ;if input present, clear it call bdos initzz: lxi h,tbuff ;if arguments given, process them. lxi d,comlin ;get ready to copy command line mov b,m ;first get length of it from loc. base+80h inx h jz i2 ora a jz i5 ;if null byte, done with list cpi '"' jnz i2a ;quote? sta tmp1 ;yes. set "in a string" flag jmp i2b i2a: dcx h i2b: mov a,l ;ok, HL is a pointer to the start stax b ;of an arg string. store it. inx b mov a,h stax b inx b inx d ;bump arg count i3: mov a,m inx h ;pass over text of this arg ora a ;if at end, all done jz i5 push b ;if tmp1 set, in a string mov b,a ; (so we have to ignore spaces) lda tmp1 ora a mov a,b pop b jz i3a 0 inx h dcr b jnz i6 ENDIF IF NOT CPM ;if not under CP/M, force ARGC value lxi h,1 ; of one. push h ENDIF xra a sta ungetl ;clear the push-back byte sta lastc ;and last character byte lhld tmp2 pchl ;all done initializing. ; ; General purpose error value return routine: ; verror: lxi h,-1 ;general error handler...just ret ;returns -1 in HL ; ; Here are file I/O handling routines, only needed under CP/M: ; ; ; Close any open files and reboot: ; vexit: IF CPM ;if under CP/M, close all open files mvi a,7+nfcbs ;start with largest possible fd exit1: push psw ;and scan all fd's for open files call vfgfd ;is file whose fd is in A open? jc exit2 ;if not, go on to next fd mov l,a ;else close the associated file mvi h,0 push h call vclose pop h exit2: pop psw dcr a ;and go on to next one cpi 7 jnz exit1 ENDIF jmp exitad ;done closing; now reboot CP/M or whatever. ; ; Close the file whose fd is 1st arg: ; IF CPM ;hercpi 255 ;if 255 comes back, we got problems lxi h,0 rnz ;return 0 if OK dcx h ;return -1 on error ret ; ; Determine status of file whose fd is in A...if the file ; is not open, return C flag set, else clear C flag: vfgfd: call setdma mov d,a sui 8 rc ;if fd < 8, error cpi nfcbs cmc ;don't allow too big an fd either rc push d mov e,a ;OK, we have a value in range. Now mvi d,0 ; see if the file is open or not lxi h,fdt dad d mov a,m ani 1 ;bit 0 is high if fntly logged disk) jnz setf1 ldax d ;oh oh...we have a disk designator call mapuc ;make it upper case sui '@' ;and fudge it a bit inx d inx d setf1: mov m,a inx h call setnm ;now set filename and pad with blanks ldax d cpi '.' ;and if an extension is given, jnz setfcb2 inx d setfcb2 mvi b,3 ;set the extension and pad with blanks call setnm xra a ;and zero the appropriate fields of the fcb mov m,a lxi d,20 dad d mov m,a inx h jmp setfcb3 ;finish up elsewhere to ;skip rest of filename if B chars already found call legfc rc inx d jmp setnm3 pad: mvi a,' ' ;pad with B blanks pad2: mov m,a ;pad with B instances of char in A inx h dcr b jnz pad2 pop b ret ; ; Test if char in A is legal character to be in a filename: ; legfc: call mapuc cpi '.' ; '.' is illegal in a filename or extension stc rz cpi ':' ;so is ':' stc rz cpi 7fh ;delete is no good stc rz cpi '!' ;if less than exclamation pt, not legal char ret ero, then it returns the address ; of the fcb corresponding to an open file whose ; fd happens to be the value in A, or C set if there ; is no file associated with fd. ; vfgfcb: push b call setdma ora a ;look for free slot? mov c,a jnz fgfc2 ;if not, go away mvi b,nfcbs ;yes. do it... lxi d,fdt lxi h,fcbt mvi c,8 fgfc1: ldax d ani 1 mov a,c jnz fgfc1a ;found free slot? pop b ;yes. all done. ret fgfc1a: push d lxi d,36 ;fcb length to accommodate random I/O dad d e comes a lot of CP/M stuff... vclose: call setdma ;library function just jumps here. call ma1toh ;get fd in A call vfgfd ;see if it is open jc verror ;if not, bitch mov a,m ani 4 jz close2 ;if not open for write, don't bother to close push h ;save fd table entry addr call ma2toh ;move arg1 to A push b call vfgfcb ;get the appropriate fcb address xchg ;put it in DE mvi c,16 ;get BDOS function # for close call bdos ;and do it! pop b pop h close2: mvi m,0 ;close logically ile is open stc pop d mov a,d rz ;return C set if not open cmc ret ;else reset C and return ; ; Set up a CP/M file control block at HL with the file whose ; simple null-terminated name is pointed to by DE: ; Format for filename must be: "[white space][d:]filename.ext" ; vsetfcb: call setdma ;set up an fcb at HL for filename at DE push b call igwsp ;ignore blanks and tabs mvi b,8 push h inx d ldax d dcx d cpi ':' ;default disk byte value is 0 mvi a,0 ; (for currekeep addresses consistent ;with prior releases ; ; This routine copes up to B characters from memory at DE to ; memory at HL and pads with blanks on the right: ; setnm: push b ldax d cpi '*' ;wild card? mvi a,'?' ;if so, pad with ? characters jz pad2 setnm2: ldax d call legfc ;next char legal filename char? jc pad ;if not, go pad for total of B characters mov m,a ;else store inx h inx d dcr b jnz setnm2 ;and go for more if B not yet zero pop b setnm3: ldax d ;else good enough ; ; Map character in A to upper case if it is lower case: ; mapuc: cpi 'a' rc cpi 'z'+1 rnc sui 32 ;if lower case, map to upper ret ; ; Ignore blanks and tabs at text pointed to by DE: ; igwsp: dcx d igwsp1: inx d ldax d cpi ' ' jz igwsp1 cpi 9 jz igwsp1 ret ; ; This routine does one of two things, depending ; on the value passed in A. ; ; If A is zero, then it finds a free file slot ; (if possible), else returns C set. ; ; If A is non-zpop d inx d inr c dcr b jnz fgfc1 fgfc1b: stc pop b ret ;return C if no more free slots fgfc2: call vfgfd ;compute fcb address for fd in A: jc fgfc1b ;return C if file isn't open sui 8 mov l,a ;put (fd-8) in HL mvi h,0 dad h ;double it dad h ;4*a mov d,h ;save 4*a in DE mov e,l dad h ;8*a dad h ;16*a dad h ;32*a dad d ;36*a xchg ;put 36*a in DE lxi h,fcbt ;add to base of table dad d ;result in HL mov a,c ;and return original fd in A pop b ret setdma: push d ;just a preventative measure, push b ;since the default I/O buffer push psw ;tends to magically change push h ;around by itself when left mvi c,26 ;in CP/M's hands !! lxi d,tbuff call bdos pop h pop psw pop b pop d ret ENDIF ;end of CP/M-related file I/O routines IF NOT CPM main: equ $ ;where main program resides when not under CP/M ;(under CP/M, the data area comes first) ENDIF ; ; Ram area: ; IF NOT CPM ;if not under CP/M, use custoo the ;storage allocator tmp: equ room ;this is misc. garbage space tmp1: equ room+1 tmp2: equ room+2 tmp2a: equ room+4 ungetl: equ room+6 ;where characters are "ungotten" lastc: equ room+7 ;last char typed ; ;-------------------------------------------------------------------------- ; The following data areas are needed only if running under CP/M: ; IF CPM ; ; The fcb table (fcbt): 36 bytes per file control block ; fcbt: ds 36*nfcbs ;reserve room for fcb's (extra byte for IMDOS) e "argv" paramater points here (well, ;actually to 2 bytes before arglst). Thus, ;up to 30 parameters may be passed to "main" ENDIF ;(enough for you, Andy?) ; ; End of CP/M-only data area ;--------------------------------------------------------------------------- IF CPM main: equ $ ;where "main" program will be loaded under CP/M ENDIF end ointed to by entries ;in arglst ; ; This is where "init" places the array of argument pointers: ; arglst: ds 60 ;th /* "CONVERT" WRITTEN BY LEOR ZOLMAN THIS PROGRAM CONVERTS REGULAR C SOURCE FILES INTO A FORMAT SUITABLE FOR EDITING ON THE TRS-80 (OR ANY UPPER-CASE-ONLY SYSTEM.) SINCE THERE ARE QUITE A FEW ASCII CHARACTERS THAT NEED TO BE REPRESENTED EVEN THOUGH THEY DON'T SHOW UP ON UPPER-CASE-ONLY SYSTEMS, A SPECIAL NOTATION HAS BEEN CREATED FOR REPRESENTING THESE CHARACTERS. THE POUND SIGN IS USED AS A SORT OF 'SHIFT' KEY, WITH THE LETTER FOLLOWING THE POUND SIGN DENOTING THE SPECIAL CHARACCKSLASH (FOR ESCAPE SEQUENCES)(5C HEX) #U UNDERSCORE (5F HEX) FOR EXAMPLE, THE COMMAND A>CONVERT FOO.C BAR.CT WILL EXPECT FOO.C TO BE A NORMAL C SOURCE FILE ON DISK, AND WILL CONVERT IT INTO A FILE NAMED BAR.CT. THE FILE BAR.CT MAY THEN BE EDITED TO YOUR TASTE, BUT REMEMBER TO PREPROCESS IT WITH "CC0T" BEFORE APPLYING THE C COMPILER. AS YOU MAY HAVE GATHERED FROM ALL THIS, THE LANGUAGE "C" WAS NEVER INTENDED TO BE IMPLEMENTED ON A SYSTEM HAVING UPPER-CASE ONLY; NEVERTHELESS, Hm ram area address org ram ENDIF room: ds 30 ;room for random stuff pbase: ds 2 ;screen-DMA address ysize: ds 2 ;screen width xsize: ds 2 ;screen height psize: ds 2 ;screen length rseed: ds 8 ;the random generator seed args: ds 14 ;"arghak" puts args passed on stack here. iohack: ds 6 ;room for I/O subroutines for use by "inp" ;and "outp" library routines allocp: ds 2 ;pointer to free storage for use by "sbrk" func alocmx: ds 2 ;highest location to be made available t ; ; The fd table: one byte per file specifying r/w/open as follows: ; bit 0 is high if open, low if closed ; bit 1 is high if open for read ; bit 2 is high if open for write ; (both b1 and b2 may be high) ; fdt: ds nfcbs ;one byte per fcb tells if it is active, r/w, etc. ; ; The command line is copied here by init: ; comlin: ds 131 ;copy of the command line pointed to by entries ;in arglst ; ; This is where "init" places the array of argument pointers: ; arglst: ds 60 ;thTER NEEDED. NOTE THAT THE C COMPILER DOES NOT RECOGNIZE THIS SPECIAL SCHEME, AND BEFORE YOU CAN COMPILE A SOURCE FILE CONTAINING THE SPECIAL CODES YOU MUST PREPROCESS THE FILE USING THE "CC0T" COMMAND. THE SPECIAL CODES AND THE CHARACTERS THEY REPRESENT ARE: #L LEFT BRACKET (FOR SUBSCRIPTING) (5B HEX) #R RIGHT BRACKET (5D HEX) #C CIRCUMFLEX (BITWISE "NOT") (7E HEX) #H UP-ARROW (EXCLUSIVE "OR" OPERATOR) (5E HEX) #V VERTICAL VAR (LOGICAL AND BITWISE "OR") (7C HEX) #B BAERE IS A WAY FOR IT TO BE DONE. THIS PROGRAM IS RATHER SIMPLE, AND THUS IT WILL NOT RECOGNIZE THAT SPECIAL CHARACTERS IN QUOTES ARE NOT SUPPOSED TO BE CONVERTED. */ #INCLUDE "BDSCIO.H" #DEFINE LEFTCURLY 0X7B #DEFINE RIGHTCURLY 0X7D #DEFINE LEFTBRACK 0X5B #DEFINE RIGHTBRACK 0X5D #DEFINE CIRCUM 0X7E #DEFINE UPARROW 0X5E #DEFINE VERTIBAR 0X7C #DEFINE BACKSLASH 0X5C #DEFINE UNDERSCORE 0X5F CHAR IBUF[BUFSIZ], OBUF[BUFSIZ]; MAIN(ARGC,ARGV) INT ARGC; CHAR *ARGV[]; BEGIN INT FD1, FD2; CHAR C; IF (ARGC != 3) BEGIN PRINTF("USAGE: CONVERT OLD NEW \N"); EXIT(); END FD1 = FOPEN(ARGV[1],IBUF); IF (FD1 == ERROR) BEGIN PRINTF("NO SOURCE FILE.\N"); EXIT(); END FD2 = FCREAT(ARGV[2],OBUF); IF (FD2 == ERROR) BEGIN PRINTF("CAN'T OPEN OUTPUT FILE.\N"); EXIT(); END WHILE ((( C = GETC(IBUF)) != CPMEOF) && C != 255) BEGIN SWITCH (C) BEGIN CASE LEFTCURLY: PUTST(" BEGIN "); BREAK; CASE RIGHTCURLY: PUTST(" END "); BREAK; CASE LEFTN WHILE (*STRING) PUTC2(*STRING++); END PUTSPEC(C) CHAR C; BEGIN PUTC2('#'); PUTC2(C); END PUTC2(C) CHAR C; BEGIN IF (PUTC(C,OBUF) < 0) BEGIN PRINTF("OUTPUT WRITE ERROR (DISK FULL?)\N"); EXIT(); END END ; CASE UNDERSCORE: PUTSPEC('U'); BREAK; DEFAULT: PUTC2(TOUPPER(C)); END END IF (C==255) C = CPMEOF; /* DIGITAL RESEARCH....WOW. */ PUTC2(C); FFLUSH(OBUF); FCLOSE(OBUF); FCLOSE(IBUF); END PUTST(STRING) CHAR *STRING; BEGI /* CC0T.C -- TRS-80 C PREPROCESSOR WRITTEN BY LEOR ZOLMAN THIS PROGRAM TAKES, AS INPUT, A C SOURCE FILE WRITTEN USING THE SPECIAL "POUND SIGN" ENCODING FOR CHARACTERS WHICH ARE UNPRINTABLE (AND UNENTERABLE) ON THE EARLY MODEL TRS-80 COMPUTERS. WHEREVER CC0T FINDS A SEQUENCE #X IN THE INPUT FILE, WHERE "X" IS ONE OF THE SPECIAL CHARACTERS AS OUTLINED IN THE SOURCE FOR CONVERT.C, THEN THE TWO-CHARACTER SEQUENCE IS CONVERTED TO A SINGLE CHARACTER AS REQUIRED BY THE BDS C COMPILER. IF (FD1 == ERROR) BEGIN PRINTF("CANNOT OPEN INPUT FILE.\N"); EXIT(); END FD2 = FCREAT(ARGV[2],OBUF); IF (FD2 == ERROR) BEGIN PRINTF("CANNOT OPEN OUTPUT FILE.\N"); EXIT(); END WHILE ((( C = GETC(IBUF)) != CPMEOF) && C != 255) BEGIN IF (C != POUND) PUTC2(C); ELSE SWITCH(C = GETC(IBUF)) BEGIN CASE 'B': PUTC2(BACKSLASH); BREAK; CASE 'L': PUTC2(LEFTBRACK); BREAK; CASE 'R': PUTC2(RIGHTBRACK); BREAK; CASE 'C': PUTC2(CIRCUM); FOPEGETZUNGET^GETFCREAMPUTPUTFFLUS[FCLOSATOSTRCASTRCMP STRCP STRLEA ISALPH ISUPPE ISLOWE ISDIGIJ ISSPACŀ TOUPPEҼ TOLOWE QSORT _SW INIT9INIT–GETVAABrMA؞MIPRINTSCAN?FPRINTƅFSCANSPRINT.SSCANZ_SP҈_GV8_USPұ_SCu_IG_BoPUT3FGETyFPUTӥSWAPI*-bage in file or bug in C Sorry...ran out of memory. Break it up! Encountered EOF unexpectedly Bad argument list to spare Include @The function "" is too complex; break it BRACK: PUTSPEC('L'); BREAK; CASE RIGHTBRACK: PUTSPEC('R'); BREAK; CASE CIRCUM: PUTSPEC('C'); BREAK; CASE UPARROW: PUTSPEC('U'); BREAK; CASE VERTIBAR: PUTSPEC('V'); BREAK; CASE BACKSLASH: PUTSPEC('B'); BREAK; CASE UNDERSCORE: PUTSPEC('U'); BREAK; DEFAULT: PUTC2(TOUPPER(C)); END END IF (C==255) C = CPMEOF; /* DIGITAL RESEARCH....WOW. */ PUTC2(C); FFLUSH(OBUF); FCLOSE(OBUF); FCLOSE(IBUF); END PUTST(STRING) CHAR *STRING; BEGITHE RESULTANT FILE MAY THEN BE COMPILED WITH CC1, CC2, ETC. */ #INCLUDE "BDSCIO.H" #DEFINE POUND 0X23 #DEFINE LEFTBRACK 0X5B #DEFINE BACKSLASH 0X5C #DEFINE RIGHTBRACK 0X5D #DEFINE CIRCUM 0X7E #DEFINE VERTIBAR 0X7C #DEFINE UNDERSCORE 0X5F #DEFINE UPARROW 0X5E CHAR IBUF[BUFSIZ], OBUF[BUFSIZ]; MAIN(ARGC,ARGV) INT ARGC; CHAR *ARGV[]; BEGIN INT FD1, FD2; CHAR C; IF (ARGC != 3) BEGIN PRINTF("USAGE: CC0T OLD NEW \N"); EXIT(); END FD1 = FOPEN(ARGV[1],IBUF); BREAK; CASE 'V': PUTC2(VERTIBAR); BREAK; CASE 'U': PUTC2(UNDERSCORE); BREAK; CASE 'H': PUTC2(UPARROW); BREAK; DEFAULT: PUTC2(POUND); PUTC2(C); END END IF (C == 255) C = CPMEOF; PUTC2(C); FFLUSH(OBUF); FCLOSE(OBUF); FCLOSE(IBUF); END PUTC2(C) CHAR C; BEGIN IF (PUTC(C,OBUF) < 0) BEGIN PRINTF("OUTPUT WRITE ERROR (DISK FULL?)\N"); EXIT(); END END DEFFOPEF!9DM͐!͐s#rz0!͐##w#w͐~#fo)GETCHABDOREA !9DM͐|#͐+++|:!͐##^#Vr+szd͐^#Vr+sn&!͐͐~#fo `is#r!ڕ!͐##6#6͐͐s#r͐^#Vr+sn&!9 !-48Kb{UNGETCi!9DM͐|%! n&͐##~#fo=!͐^#Vr+s! ns͐##^#Vr+s! 6GET[!9DM͐`is#rzL͐! s#rzL͐?͐R!R!9 (6JPUNLINCREAd !9DM͐͐͐s#rz7!͐͐s#r͐##6#6͐~#fo%0PUTCHABDOWRIT !9DM͐+|,! n&͐++|J! n&!͐+++|i! n&!͐##^#Vr+szʚ͐^#Vr+s! ns&!͐͐~#fo !͐##6#6͐͐s#r͐^#Vr+s! ns& '5DTczPUTQISDIGI!9DM`iw#w! 6#6͐ n! s{ 9! n} G! ^#Vr+s! n}-f! 6#6! ^#Vr+s! ^#Vr+sn! s|ʧ͐ ?! nѯg`is#rf͐͐?÷!9 ,7EP~b!9DM͐`is#r͐n}*! ^#Vr+s! ^#Vr+s͐ns! ^#Vr+sn}*͐Y!9(PW!9DM`iw#w! ~#fo͐n! ~#fo͐n}Z! ~#fo`i^#Vr+sn}W!Æ! ~#fo͐n! ~#fo͐nѯgWÆ!93OUXH!9DM͐`i+ISUPPE8!9DM! n&|-! n& 2! n&+_SWg!9DM͐͐?! s#r͐! s#r͐^͐͐?`is#r͐͐! s#r͐͐! s#r͐ ! s#r͐͐ F͐͐ ! s#r͐|.͐͐͐͐͐!!9~#fo.͐͐͐͐͐! ~#fo͐s#rî! ~#fo͐s#rÈ! ~#fos#r6!9 A,D\V!9DM! ^#Vr+szM͐n`is!PPUT. !y9DM! `i`i!9"GET_SC1 !y9DM`i! `i(!9!&_SPFPUT7 !y9DM! `i͐`i.!9',FGET_SCB !y9DM͐`i|)!9! `i9!9!'27_SP!9DM! ͐_SC!!9DM! ͐_USPISDIGI_GVTOUPPEF!i9DM! ^#Vr+s~#fo! s#r͐! s#r! ^#Vr+sn`is{7`in}%! ^#Vr+s!͐6! ! s#r! n}®! ^#Vr+s!Ү! ^#Vr+s6 Ç͐! ^#Vr+sns{! ^#Vr+sî! n}! ^#Vr+s!! ^#Vr+s6 ! ^#Vr+s`ins4! ^#Vr+s`ins>͐6!9'S\ $),9qz)2i 5ISDIGIc!9DM`iw#w͐~#fon&}S͐ ?͐^#Vr+snѯg`is#r͐Z!9!'QX_USP!9DM͐͐ V͐^#Vr+s͐ C͐0K͐!9DM͐͐)|J͉͐͐|J͐! ';BWRITMOVMESEEM !9DM͐'!D͐##~#fo@!D͐##~#fo`is#r͐͐͐~#fo͐ʍ!D͐+?`is#r͐##~#fo|!͐͐͐͐##~#fo͐s#r͐~#fo͐s#r!!͐~#fo D͐##6#6͐͐s#r!D!9 %8>vBCLOS!9DM͐~#fos#r! ^#Vr+s! ^#Vr+sns{8͐?!936==!9DM`iw#w! ^#Vr+sn}-`i^#Vr+s͐4!9+2ISUPPEISLOWE: !9DM! n&|ͣ6! n&|ͣ#./!9DM! n&Aͯ+! n&Zͯ/!9DM! n&aͯ+! n&zͯ/!9DM! n&0ͯ+! n&9ͯ3!9DM! n} ͝/! n} ͝/! n} ͝"ISLOWE8!9DM! n&|-! n&2! n& ^#Vr+s͐ ns! ^#Vr+s`ins!9KGETVAJ!9DM! `is#r!A! ^#Vr+s͐s#r!9$?GETVAF!9DM! `is#r!=! ^#Vr+s͐s!9$;ATOj!9DM͐~#fon}"!a͐~#fo`is#r͐~#fon}Z͐^#Vr+sn},Z5͐a!9 ,AUX_#!9DM͐|͐͐'!9DM͐͐͐#͐'!9DM͐͐͐#͐_S! ! s#r! 6#6! s! s͐n}-š! ^#Vr+s! 4͐n&}ʷ! ú!! s#r! ^#Vr+sn`is{.! ! s#r! 4! ^#Vr+sn`is`in& }D.UjXsO|CS ͐~#fo|j! ^#Vr+s6-͐͐~#fos#r! ^#Vr+s! 6 Â! 6Â! 6! ~#fo! n&! ^#Vr+s~#fo! ѯgs#rk! ^#Vr+s! ^#Vr+s~#fos! ^#Vr+sk! n}! 6#6! ^#Vr+s~#fo! s#r͐n}k͐|k! ^#Vr+s! ^#Vr+sns! ^#Vr+s!7s!&á͐ ͐͐ ͉͐`is͐ ͐͐ )͐`in&#&á!96AToISSPAC_IGTOUPPE_BPOK!9DM! ^#Vr+s~#fo! s#r͐! s#r! 6! ^#Vr+sn! s{ ͐n}t! n&! n&|ʈG! n}%! n! ʸ! n&! ^#Vr+s! 6#6! 6 `i6! ^#Vr+sn! s{* `i4! ^#Vr+sn! s! n& }X8OADJUpSCʏ! 6p! 6p! -p! 6#6! ^#Vr+s! w#w! n&!  #|Ÿ! n&! n&! ^#Vr+sn& ! s{͐ ! nѯg?! nѯg! s#rß! ^#Vr+s! ͐ ~#fo! s#r! ^#Vr+sn! s{m! n͐n}M! ^#Vr+sm`in}j! ^#Vr+s! ns`in}Œ! 4͐6! ^#Vr+sG`in}½͐n&! ^#Vr+s~#fo! 4! ^#Vr+sG! n&`in}! ^#Vr+s~#fo͐ ͐?s#r! 4G! n&!91^hr}$).36?HPZ,=KSk++n} •! ^#Vr+s6 `i^#Vr+sz͐ ! s#rz͐N͐͐ ͐͐6͐ !9*<FLfwPUTj!9DM! ^#Vr+sn`is{[`in} <͐! ͐`in&#|X!a!a!9 #,8HPVY_OPEPRINTREACLOS!9DM!͐`is#rz`͐!<TSwapin: cannot open %s !!'͐͐ |͐!ÞSwapin: read error on %s ͐ !͐ !GETCHAKBHICUNGETC]PUTCHAqPUTCȲGETRAN SRAN:SRANDNRANCSSETMEMOVME*CAĽCALLINOUTPEEPOK SLEEPAUSWSETFCkREAĔWRITOPEΌCLOSCREAUNLIN'SEEATELRENAMFABORT FCBADDh EXI} BDOӅ BIOӝ CODEN EXTERN ENDEX TOPOFME EXE EXEC SBR RSVST˻ SETPLO CLRPLO LIN PLO TXTPLO RREA2 RWRIT RSEEQRTEĻRSRERCFSIF  END OF ASSEMBLY G:%x-!%~--.-*P0*%O {-z-.*P0"%!%^4!%w.*"*"*"|P*!Z ##7""":2!6#]! ~W!~w# vzҗ!w# o g!?/w#ª+~g:oŇ#*1CFILQV[bhq|o&7**:Ozq#Q7*|DM**K/><#~# x# >Goy$$![7*!*&*!:&*!, !\&!TELk  )6! ) , 7:O*o`$7*+++:G_*DM!o&****+7*!9s ISSPACF!9DM͐~#fon`is|5͐^#Vr+s`in&=!9%3;ISALPHTOUPPEISDIGI !9DM! n&! s|D! n&sl! n& }g! n&sl!! n! n&+ҋ!Ô! n& *0BMSePUTCHA2!9DM͐n}0! ^#Vr+sn&*.GETUNGET !9DM`i6#6͐ ! s#r͐ ! s#r!H͐#|N!! ^#Vr+s͐s{ ˜͐͐ #ҕ!9!/7:V^ow |͐!ÞSwapin: read error on %s ͐ !͐ !*<FLfwPUTj!9DM! ^#Vr+sn`is{[`in} <͐! ͐`in&#|X!a!a!9 #,8HPVY_OPEPRINTREACLOS!9DM!͐`is#rz`͐!<TSwapin: cannot open %s !!'͐͐ |͐!ÞSwapin: read error on %s ͐ !͐ !FORC5:wo2w&!o 2 . &%:w&o o&:wo2w&6    _ * & _    - 6  #F#x(~#&(*>H7*|g}o"zg{o"|g i |""E ! ###""  Wait a few seconds, and type a CR: $(PUT) !xV ###"" PUT7*|& n& 7*:w1 #+| 'z   /   7*~#  +*&! v7:)~:,"s!"u*|V**s[! ~V6*u*+"*"*u#"u"(HRth7:)~:,"s!"u*|*uf*~#="*s*uf#"u*+"*3CVdD7,2q*&:q):<=<=r:qo&.4#UNLINOPE1 7*& 6C#6O#6M/[!w#w#*w#<:**S*!Âѷʤ!Å(0BN7*!9& 6C#6O#6M/*|E!\&W!\&*|W!!l&!~#foʏ> +͆#~t##ha{ !p!*w#œ:**ʳ*!ѷ! (5CRou~1 *&+*|/g}/o#9*&#"z{  "7*"*"*"k"**6 #z7:O:GPY|:O:G|`i#z͘8{͘8K|PY##|^|]^%}o}no-|G}O*yogH :wɷ/<&+059<BFRW[chl"7:**<=:_:w57**k***:'~.ɰ#'#*37:)~:,"s!"u*|*u/**s!ѷfUO! ! ~a6*u*+"*"*u#"u*s! N#Fp+q"+EJ]v7:)~:,"s!"u* :!B/< !:!]:!G8!d+B!d822!d!d!=͌4+~q374!f!U!:@w#> w! !Uf! U>2!:G~CFKNSV\adhknquz}  %0:EQZcgjnqvy|SV\adh; ; BDS C Standard Library Machine Language Functions v1.42 11/22/80 ; ; Many thanks to Leo Kenen for modifying the CMAC.LIB package to ; handle multiple functions per source file. ; maclib bds maclib cmac direct define getchar define kbhit define ungetch define putchar define putch define gets define rand define srand define srand1 define nrand define csw define setmem define movmem define call define calla define inp define outp define peek define pon-zero, an ungotten character ; is assumed to be there, and it is returned. ; ; If a control-C is typed, CP/M is rebooted. ; If a control-Z is typed, a -1 is returned. ; If a return is typed, "newlin" (really linefeed) is returned ; and a CR-LF combo is echoed to the console output. ; prelude getchar lda ungetl ;any character pushed back? ora a mov l,a reloc jz,gch2 xra a ;yes. return it and clear the pushback sta ungetl ;byte in C.CCC. mvi h,0 ret gch2: push b mvi c,coni|*u7*~#?"*s"*u#"u*+"*s! N#Fp+q*3EtG7:,?*:4="4#?*!s#r!^#V .7:,!^#V7:,$!~#fo7:,#!~#fo7!&:_!og(2;DMV]͌̀m.mv.ḿ.m.]ͫ*!6#]]*!6#]*]:2!! ­2!)!5:!4+3:!! !]!5 >!~!=!]!6!)͆!4H!36>2!~p!>0*w#"!5ʹ͆U!5! c2c2!]!)!)!=!)*6E#:/<26-#: 2 >0w#:0w#6!]́!5!]́!47~/w# ɯ# !ɧ~w# *ɧ~w+ 4ɧw# >ɧ# J6# Uw# ]:2x!!:w:!ffffP "),/$WZ369<?BEHKoke define sleep define pause define setfcb define read define write define open define close define creat define unlink define seek define tell define rename define fabort define fcbaddr define exit define bdos define bios define codend define externs define endext define topofmem define exec define execl define sbrk define rsvstk enddir ; ; Getchar: ; ; Function to get and echo a character from the console input: ; ; If location "ungetl" is nn call bdos pop b cpi cntrlc ;control-C ? jz base ;if so, reboot. cpi 1ah ;control-Z ? lxi h,-1 ;if so, return -1. rz mov l,a cpi cr ;carriage return? reloc jnz,gch3 push b mvi c,conout ;if so, also echo linefeed mvi e,lf call bdos pop b mvi l,newlin ;and return newline (linefeed).. gch3: mvi h,0 ret postlude getchar ; ; Kbhit: ; ; int kbhit(); ; ; Returns true if a character is present on the console input; ; else returns false. ; prelude kbhit lda ungetl ;any character ungotten? mvi h,0 mov l,a ora a rnz ;if so, return true push b mvi c,cstat ;else interrogate console status call bdos ani 1 mov l,a ;return 1 if character ready, else 0 mvi h,0 pop b ret postlude kbhit ; ; Ungetch: ; ; ungetch(c) ; char c; ; ; Un-get a character by putting it at location "ungetl": ; Returns non-zero if there was already a character pushed ; back since the last getchar call. ; prelude ungetch lda ungetl mov l,a pnewline? reloc jnz,put1 mvi e,cr ;yes...put out CR-LF call bdos mvi c,conout mvi e,lf call bdos reloc jmp,put2 put1: mov e,a ;not newline. just put out the character call bdos put2: mvi c,cstat ;now, is input present at the console? call bdos ani 1 reloc jnz,put3 pop b ;no...all done. ret put3: mvi c,conin ;yes. sample it (this will always echo the call bdos ; character to the screen, alas) cpi cntrlc ;is it control-C? jz base ;if so, abort and reboot pop b ;elole and put it and put it (null terminated) ; at the given buffer. The buffer should be several characters longer than ; the expected maximum length input line, since the BDOS call for getting ; a buffer is used...and it never hurts to overanticipate a user's verbosity! ; prelude gets call ma1toh ;get addr of buffer push b ;save frame pointer mvi m,0ffh ;max length (assume user knows enough to declare ;a big enough buffer...not that gets can KNOW how ;big it is... mvi c,getlin xr character stax d inx h inx d dcr b reloc jmp,copyl ;loop till all done gets2: xra a ;place terminating null into buffer stax d pop h ;pop start of buffer again, into HL pop b ;get back stack frame pointer ret ;and go home. postlude gets ; ; Rand: ; ; rand(); ; ; Original random function, uses 32-bit seed at rseed: ; prelude rand lhld rseed xchg mvi a,48h ana e reloc jz,rand1 reloc jpe,rand1 stc rand1: lhld rseed+2 mov a,h ral mov h,a m1 call bdos pop h inx h inx h inx h ani 1 reloc jz,srand3 shld rseed shld rseed+2 mvi c,conout mvi e,cr call bdos mvi c,conout mvi e,lf call bdos mvi c,conin ;clear the character call bdos pop b ret stg1: db 'Wait a few seconds, and type a CR: $' postlude srand ; ; Srand1: ; ; srand1(string); ; ; New srand, which "hides" itself in a string prompt ; prelude srand1,puts call ma1toh push h exrel call,puts ;print prompt string pop h push b ush h call ma2toh sta ungetl pop h mvi h,0 ret postlude ungetch ; ; Putchar: ; ; putchar(c); ; ; Write a character to the console output. Turn "newline" ; (really linefeed) into a CR-LF combo. Also check if anything ; is present on the console input; if so, check to see if it is ; a control-C, and if so, reboot CP/M. (This allows a general ; interrupt during profuse unwanted output.) ; prelude putchar call ma1toh ;get character in A push b mvi c,conout cpi newlin ;se ignore it. ret postlude putchar ; ; Putch: ; ; putch(c) ; char c; ; ; Write a char to console output without testing ; for control-C on console input. ; prelude putch call ma1toh push b mvi c,conout mov e,a cpi newlin ;newline? reloc jnz,putch1 ;if not, just put out the character mvi e,cr ;if so, put out CR-LF call bdos mvi c,conout mvi e,lf putch1: call bdos pop b ret postlude putch ; ; Gets: ; ; gets(buffer); ; ; Get a string from the conschg ;put buffer addr in DE push d ;save it push d ;a coupla times push d ;and once more call bdos ;get the input line mvi c,conout mvi e,lf ;put out a linefeed after the automatic cr call bdos pop h ;get back buffer address inx h ;point to returned char count mov b,m ;set B equal to char count inx h ;HL points to first char of line pop d ;DE points to start of buffer copyl: mov a,b ;done copying? ora a reloc jz,gets2 ;if so, get out of loop mov a,m ;else copy anotheov a,l ral mov l,a shld rseed+2 mov a,d ral mov h,a mov a,e ral mov l,a shld rseed mov a,h ani 7fh mov h,a ret postlude rand ; ; SRAND: ; ; srand(n); ; ; Initialize random number generator. If n==0, prints ; message and waits for a key; else, uses n as seed. ; prelude srand call ma1toh mov a,h ora l reloc jz,srand2 shld rseed shld rseed+2 ret srand2: reloc ,stg1 push b mvi c,9 call bdos lxi h,0bdbdh srand3: push h mvi c,1 lxi h,5678h sr1a: push h mvi c,cstat call bdos pop h inx h inx h inx h ani 1 reloc jz,sr1a shld rseed shld rseed+2 pop b ret postlude srand1 ; ; Nrand: ; ; The new random number routine, adapated from the CDC 6600 ; emulator written by Paul Gans ; Calling parameters: nrand(n [,s1, s2, s3]); ; where if n == -1 then s1,s2,s3 is the 48-bit seed; ; n == 0 then do interactive initialization as per srand1 ; else the next value in the sequence is returned. ; prelude nrand,puts call arghak lhld arg1 ;get n (1st arg) mov a,h ana l cpi 255 ;was it -1 (set seed) ? reloc jnz,nrand1 lhld arg2 ;copy seed reloc shld,seed lhld arg3 reloc shld,seed+2 lhld arg4 reloc shld,seed+4 ret ;all done nrand1: push b mov a,h ;look at first arg again ora l reloc jnz,nrand3 ;is it 0 (randomize) ? lhld arg2 push h ;yes. print out string exrel call,puts ;call puts pop d lxi h,5a97h ;yes. start w/something odd nrand2: push h mvi c,cst0 inx h dcr b reloc jnz,randm1 lxi b,6 ;set byte counter randm2: reloc ,plier-1 dad b ;make addr of lsb of PLIER mov a,m ;PLIER byte push b ;save byte counter mvi b,8 ;set bit counter randm3: mov d,a ;save PLIER byte reloc ,prod ;shift whole PROD left one bit mvi c,6 xra a randm4: mov a,m ;get byte ral ;shift left mov m,a ;put byte inx h dcr c reloc jnz,randm4 mov a,d ;recover PLIER byte ral ;look at current high bit reloc jnc,randm6 cma aci 0 mov m,a inx h inx d dcr b reloc jnz,randm7 dcx h ;put the two high order bytes mov a,m ;into HL for return to C, not ani 7fh ;neglecting to zero the high mov h,a ;order bit so a positive int reloc lda,seed+4 ;is returned mov l,a pop b ret plier: db 0c5h,87h,1 db 0eh,9ah,0e0h seed: db 1,0,0,0,0,0 prod: db 0,0,0,0,0,0 postlude nrand ; ; Csw: ; ; Get the value at port 255 (console switch register on ; many mainframes) ; prelude csw intter WHAT the logistics of the source and ; destination address ; prelude movmem call arghak lhld arg3 ;get block length mov a,h ora l rz ;do nothing if zero length push b mov b,h mov c,l ;set BC to length lhld arg2 ;get dest addr xchg ;put in DE lhld arg1 ;get source addr in HL reloc call,cmphd ;if source < dest, do tail-first reloc jc,tailf ;else do head-first headf: mvi a,2 ;test for Z-80 inr a reloc jpe,m8080h ;Z80? db 0edh,0b0h ;yes. do block move. pop b de movmem ; ; Call: ; ; call(addr,a,h,b,d); ; ; Call a subroutine at a given location, passing values in CPU ; registers, and accept the value returned by the subroutine ; in HL as return value. ; (See also CALLA for the counterpart of this, that accepts the ; return value in A) ; prelude call call arghak push b lhld arg5 xchg lhld arg4 mov b,h mov c,l lda arg2 reloc ,call2 push h lhld arg1 push h lhld arg3 ret call2: pop b ret postlude call ;at ;interrogate console status call bdos pop h inx h ;and keep it odd inx h ;and growing ani 1 reloc jz,nrand2 ;until user types something. reloc shld,seed ;then plaster the value all over the reloc shld,seed+2 ;seed. reloc shld,seed+4 pop b ret nrand3: reloc lda,seed ;now compute next random number. from this ori 1 ; point on, the code is that of Prof. Paul Gans reloc sta,seed ;lsb of SEED must be 1 mvi b,6 ;clear 6 PROD bytes to 0 reloc ,prod randm1: mvi m, ;0 means no add cycle push psw ;add SEED to PROD xra a mvi c,6 reloc ,prod reloc ,seed randm5: ldax d adc m mov m,a inx h inx d dcr c reloc jnz,randm5 pop psw randm6: dcr b ;test bit counter reloc jnz,randm3 ;go cycle more bits pop b ;recover byte counter dcr c ;test it reloc jnz,randm2 ;go process more bytes mvi b,6 ;complement PROD, add 1 to it, reloc ,seed ;and transfer it to SEED. reloc ,prod xra a cmc randm7: ldax d 255 mov l,a mvi h,0 ret postlude csw ; ; Setmem: ; ; setmem(addr,length,val); ; ; Fill a block of memory with a given constant ; prelude setmem call arghak push b lhld arg2 xchg lhld arg1 lda arg3 mov c,a inx d setm2: dcx d mov a,d ora e reloc jnz,setm3 pop b ret setm3: mov m,c inx h reloc jmp,setm2 postlude setmem ; ; Movmem: ; ; Move a block of memory, optimizing for Z-80 when ; possible and also insuring integrity of the data ; no ma ret ;and done. m8080h: mov a,m stax d inx h inx d dcx b mov a,b ora c reloc jnz,m8080h pop b ret tailf: dcx b ;tail first. Compute new source dad b ;and destination addresses xchg dad b xchg inx b mvi a,2 ;test for Z80 inr a reloc jpe,m8080t ;Z80? db 0edh,0b8h ;yes. do block move. pop b ret m8080t: mov a,m stax d dcx h dcx d dcx b mov a,b ora c reloc jnz,m8080t pop b ret cmphd: mov a,h cmp d rnz mov a,l cmp e ret postlu ; Calla: ; ; calla(addr,a,h,b,d); ; ; And here it is, the same routine as above except that the ; return value is expected in A instead of HL: ; prelude calla call arghak push b lhld arg5 ;get de value xchg lhld arg4 ;get bc value mov b,h mov c,l lda arg2 ;get a value reloc ,calla2 ;get return address push h ;push it lhld arg1 ;get address of routine push h lhld arg3 ;get hl value ret ;call routine calla2: mov l,a ;put A value in HL mvi h,0 ;clear high byte pop b ret postlude calla ; Inp: ; ; char inp(port); ; ; Sample data at an input port: ; prelude inp call ma1toh sta iohack+1 ;store as arg to ram area input subroutine call iohack ;call the subroutine to get value mov l,a ;and put into HL mvi h,0 ret postlude inp ; ; Outp: ; ; outp(port,val); ; ; Output a byte to an output port: ; prelude outp call ma1toh ;get port number sta iohack+4 ;store as arg to ram area output subroutine call ma2toha Z80). ; prelude sleep call ma1toh push b inx h sl1: dcx h mov a,h ora l reloc jnz,sl1a pop b ret sl1a: lxi d,10000 sl2: dcx d mov a,d ora e reloc jnz,sl2 push h mvi c,cstat call bdos ani 1 pop h reloc jz,sl1 push h mvi c,conin call bdos cpi cntrlc jz base pop h reloc jmp,sl1 postlude sleep ; ; Pause: ; ; Sleep until a character is typed ; (but don't sample the character) ; prelude pause push b paus1: mvi c,cstat call bdos aniit lxi h,0 ;all OK. pop b ret postlude setfcb ; ; Read: ; ; read(fd, buf, nbl) ; ; Reads nbl sectors, and fudges the next-sector ; byte in the fcb after encountering an EOF on an extent boundary, ; so "write" won't screw up in that special case. ; prelude read call arghak lda arg1 call fgfd jc error ;error if illegal fd mov a,m ani 2 ;open for read? jz error ;error if not push b lda arg1 call fgfcb shld tmp2 ;tmp2 will hold dma addr lxi h,0 ;count of are we on extent boundary? dad d ;if so, adjust for CP/M's stupidity here mov a,m ;by turning an 80h sector count into 00h. cpi 80h reloc jnz,read4 mvi m,0 ;yes. reset nr to 0...CP/M leaves it at 80h! read4: lhld tmp2a read5: pop b ret read6: lhld arg3 dcx h shld arg3 lhld arg2 lxi d,128 dad d shld arg2 lhld tmp2a inx h shld tmp2a reloc jmp,read2 postlude read ; ; Write: ; ; Format: ; write(fd, buf, nbl) ; ; Writes nbl block of data from memory at buf vi b,128 writ2: mov a,m stax d inx h inx d dcr b reloc jnz,writ2 shld arg2 ;save -> to next 128 bytes lhld tmp2 ;get addr of fcb xchg mvi c,writs ;go write call bdos ora a ;error? lhld tmp2a ;if so, return # of successfully written reloc jnz,writ3 ; sectors. inx h ; else bump successful sector count, shld tmp2a lhld arg3 ; debump countdown, dcx h shld arg3 reloc jmp,writ1 ; and go try next sector writ3: pop b ret postlude write ; ; Open: ; ; open(fi ;get data byte call iohack+3 ;output it ret postlude outp ; ; Peek: ; ; peek(addr); ; ; Return the byte value of a memory location: ; prelude peek peek: call ma1toh mov l,m mvi h,0 ret postlude peek ; ; Poke: ; ; poke(addr,val) ; ; Deposit a byte value at a memory location: ; prelude poke call arghak lhld arg1 lda arg2 mov m,a ret postlude poke ; ; Sleep: ; ; sleep(n); ; ; Sleep for a given number of tenths of a ; second (or twentieths on 1 reloc jz,paus1 pop b ret postlude pause ; ; Setfcb: ; ; Function to perform the "setfcb" function on user ; supplied text and fcb addresses. ; ; format: ; setfcb(fcbaddr,filename) ; char *fcbaddr, *filename; ; prelude setfcb call arghak push b lhld arg2 ;get pointer to name text igsp: mov a,m inx h cpi ' ' ;space? reloc jz,igsp cpi tab ;tab? reloc jz,igsp dcx h xchg ;set DE pointing to 1st non-space char lhld arg1 ;get --> fcb area call setfcb ; do # of successful sectors read shld tmp2a ; will be kept at tmp2a read2: lhld arg3 ;done? mov a,h ora l reloc jz,read4 read2a: lhld arg2 ;else read another sector xchg ;DE is dma addr mvi c,sdma call bdos ;set DMA lhld tmp2 xchg ;DE is fcb addr mvi c,reads push d ;save de so we can fudge nr field if call bdos ;we stop reading on extent boundary... pop d ; CP/M sucks! cpi 2 pop b jz error ;if error, abort push b cpi 1 reloc jnz,read6 ;EOF? read3: lxi h,32 ;yes. to file having ; descriptor fd. File must have been opened for writing. ; prelude write call arghak lda arg1 call fgfd jc error mov a,m ani 4 jz error push b lda arg1 call fgfcb shld tmp2 lxi h,0 shld tmp2a lxi d,tbuff ;80 for normal CP/M, else 4280 mvi c,sdma call bdos writ1: lhld arg3 ;done yet? mov a,h ora l lhld tmp2a ;if so, return count reloc jz,writ3 lhld arg2 ;else copy next 128 bytes down to tbuff lxi d,tbuff ;80 for normal CP/M, else 4280 mlename,mode) ; ; Open a file for raw (non-bufferred) read (0), write(1), or both(2). ; prelude open call arghak xra a call fgfcb ;any fcb's free? jc error ;if not, error sta tmp xchg lhld arg1 xchg push b call setfcb mvi c,openc call bdos cpi errorv ;successful open? pop b jz error ;if not, error lda tmp call fgfd ;get HL pointing to fd table entry lda arg2 ora a ;open for read? mvi d,3 reloc jz,open1 dcr a mvi d,5 reloc jz,open1 ;write? dcr a jnz error ;else must be both or bad mode. mvi d,7 open1: mov m,d lda tmp mov l,a mvi h,0 ret postlude open ; ; Close: ; ; close(fd); ; ; Close a file ; (jumps to the close routine built into C.CCC, the runtime package) ; prelude close jmp close ;jump to the close routine in C.CCC postlude close ; ; Creat: ; ; creat("filename"); ; ; Create a new file on the filesystem, ; then, open it for output. ; prelude creat, call arghak lhld arg1 push b rigin); ; ; Seek to a particular sector in an open file, where the new sector ; address is: ; offset if origin == 0 ; current+offset if origin == 1 ; prelude seek,tell call arghak ;copy arguments to args area lda arg1 call fgfcb jc error ;error if file not open push b push h ;save fcb address lhld arg1 push h exrel call,tell ;get r/w pointer position for the file pop d xchg ;put present pos in DE lda arg3 lhld arg2 ;get offset in HL ora a ;absolute offset? relos seek5: pop d cpi errorv reloc jz,seek3 lxi h,32 ;and set nr field dad d pop d mov a,e ani 7fh mov m,a xchg ;return new sector # in HL pop b ret postlude seek ; ; Tell: ; ; tell(fd); ; ; Return position of r/w pointer associated with a particular file: ; prelude tell call ma1toh ;get fd value in A call fgfcb jc error push b lxi d,12 dad d mov b,m ;put extent # in B lxi d,20 dad d mov c,m ;put sector # in C xra a ;rotate extent right one bitl ; ; Rename: ; ; rename(old,new) ; ; Function to rename a file: ; prelude rename call arghak push b renam: lhld arg1 xchg reloc ,wfcb call setfcb lhld arg2 xchg reloc ,wfcb+16 call setfcb reloc ,wfcb mvi c,renc call bdos pop b cpi errorv jz error lxi h,0 ret wfcb: ds 49 postlude rename ; ; Fabort: ; ; fabort(fd); ; ; Free up the file descriptor given as argument, or return ; -1 if not open: ; prelude fabort call e exit ; ; Bdos: ; ; bdos(c,de) ; ; Call to bdos routine c, with BC set to c, DE set to de.. ; ; Returns H equal to the value returned by the BDOS in B, ; and L equal to the value returned by the BDOS in A. ; ; (Note: User's Guide incorrectly says return value is 0-255) ; prelude bdos call arghak push b lda arg1 ;get C value mov c,a lhld arg2 ;get DE value xchg ;put in DE call bdos ;make the bdos call mov l,a ;move return value from A into HL mov h,b pop b ret ;a push h exrel call,unlink ;erase any old versions of file pop d mvi c,creatc lxi d,fcb call bdos cpi errorv pop b jz error lxi h,1 push h lhld arg1 push h exrel call,open pop d pop d ret postlude creat ; ; Unlink: ; ; unlink("filename"); ; ; Delete a file from the filesystem: ; prelude unlink call ma1toh push b xchg lxi h,fcb call setfcb mvi c,delc call bdos lxi h,0 pop b ret postlude unlink ; ; Seek: ; ; seek(fd, offset, ooc jz,seek2 ;if so, offset is new position dad d ;else add offset to current position seek2: mov a,l ;convert to extent and sector values rlc mov a,h ral ani 7fh sta tmp xthl lxi d,12 push h dad d cmp m ;jumping over extent boundary? reloc jz,seek5 xthl ;yes. xchg mvi c,closec ;close old extent push d call bdos pop d pop h cpi errorv reloc jnz,seek4 seek3: pop d pop b jmp error seek4: lda tmp mov m,a push d mvi c,openc ;and open new one. call bd, old b0 --> Carry mov a,b rar mov h,a ;rotated value becomes high byte of tell position mvi a,0 ;rotate b0 of extent into A rar mov b,a ;save rotated extent number in B add c ;add rotated extent number to sector number mov l,a ;and result becomes low byte of tell position mov a,c ;if both rotated extent # and sector # has bit 7 hi, ana b ;then the sum had an overflow, so... reloc jp,tell2 inr h ;bump position number by 256 tell2: pop b ;and all done. ret postlude telma1toh call fgfd jc error mvi m,0 ;clear entry in fd table lxi h,0 ret postlude fabort ; ; Fcbaddr: ; ; Return address of fcb associated with the file associated ; with the fd given as arg: ; prelude fcbaddr call ma1toh call fgfd ;is it an open file? jc error call ma1toh call fgfcb ;get fcb addr in HL ret postlude fcbaddr ; ; Exit: ; ; exit(); ; ; Close all open files and reboot (jumps to C.CCC exit routine...) ; prelude exit jmp exit postludnd return to caller postlude bdos ; ; Bios: ; ; bios(n,c) ; ; Call to bios jump table routine n, with BC set to c. n=0 for boot, ; n=1 for wboot, n=2 for const, etc. ; prelude bios call arghak push b lhld base+1 ;get addr of jump table + 3 dcx h ;set to addr of first jump dcx h dcx h lda arg1 ;get function number (1-85) mov b,a ;multiply by 3 add a add b mov e,a ;put in DE mvi d,0 dad d ;add to base of jump table push h ;and save for later lhld arg2 ;get value to be put in BC mov b,h ;and put it there mov c,l reloc ,retadd ;where call to bios will return to xthl ;get address of vector in HL pchl ;and go to it... retadd: mov l,a ;all done. now put return value in HL mvi h,0 pop b ret ;and return to caller postlude bios ; ; Codend: ; ; Returns address of byte after last byte of compiled code: ; prelude codend lhld codend ret postlude codend ; ; Externs: ; ; Returns address of start of external datld arg1 xchg lxi h,-60 dad sp push h push h push h call setfcb pop h lxi b,9 dad b mvi m,'C' inx h mvi m,'O' inx h mvi m,'M' pop d mvi c,openc call bdos cpi errorv reloc jnz,noerr pop h pop b jmp error noerr: reloc ,code lxi h,tbuff xra a mov m,a ;zero tbuff inx h mov m,a ; and tbuff+1 inx h mvi b,42 ;length of loader code segment exec2: ldax d ;copy loader down to tbuff+2 mov m,a inx h inx d dcr b reloc jnz,exec2 popra a ;end of file? jz tbuff+24h ;if not, get another sector (jz `loop1:') mvi c,sdma ;reset DMA pointer (stupid CP/M transients lxi d,tbuff ; ASSUME the DMA pointer is at 80h) call bdos jmp tpa ;and go run the program loop1: lxi h,80h dad d xchg jmp tbuff+5 ;loop address (`loop:') postlude exec ; ; Execl: ; ; Function to chain to another C-generated COM file, with ; text argument passing. ; prelude execl call arghak push b lhld arg1 xchg lxi h,-60 mov a,h ora l reloc jnz,excl0 lxi d,arg2 push d lxi h,fcb call setfcb pop h reloc jmp,excl0a excl0: xchg ;into first default fcb slot lxi h,fcb call setfcb lhld arg3 ;and stick second param string mov a,h ora l reloc jnz,excl0a lxi h,arg3 excl0a: xchg ;into second default fcb slot lxi h,fcb+16 call setfcb lxi d,tbuff+1 ;now construct command line: xra a ; zero tbuff+1 just in case there stax d ; are no arg strings lxi h,arg2 ;pointer to 1st arg stringcl1 ;and go do next string mpuc: cpi 'a' ;convert character in A to upper case rc cpi 'z'+1 rnc sui 32 ret excl3: pop h ;clean up stack lxi h,tbuff ;set length of command line mov m,b ;at location tbuff excl3a: reloc ,code0 ;copy loader down to end of tbuff lxi h,tpa-42 mvi b,42 ;length of loader excl4: ldax d mov m,a inx d inx h dcr b reloc jnz,excl4 pop b ;get back working fcb pointer lda tpa ;sequence to reset SP cpi 2ah ;are we using an lhlda area: ; prelude externs lhld extrns ret postlude externs ; ; Endext: ; ; Returns address of byte following the end of the external data area: ; prelude endext lhld freram ret postlude endext ; ; Topofmem: ; ; Returns address of last byte of available user ram: ; prelude topofmem lhld base+6 dcx h ret postlude topofmem ; ; Exec: ; ; A simpler chaining function, which doesn't support arg ; passing: ; prelude exec call arghak push b lh b ;get fcb pointer in BC lda tpa ;sequence to reset SP cpi 2ah ;are we using an lhld? lhld base+6 reloc jz,go1 ;if so, use regular bdos addr lhld tpa+1 ;else get special SP value go1: sphl lxi h,base push h ;set base of ram as return addr jmp tbuff+2 ;(`code:') code: lxi d,tpa loop: push d push b ;save fcb pointer mvi c,sdma call bdos pop d ;get fcb pointer in DE push d ;and resave it mvi c,reads call bdos pop b ;get fcb pointer back in BC pop d o;compute &nfcb for use here dad sp push h ; save for much later (will pop into BC) push h ;make a few copies for local use below push h call setfcb ;set up COM file for execl-ing pop h ;get new fcb addr lxi b,9 ;set extension to COM dad b mvi m,'C' inx h mvi m,'O' inx h mvi m,'M' pop d ;get new fcb addr again mvi c,openc ;open the file for reading call bdos cpi errorv reloc jnz,noerrr pop h pop b jmp error noerrr: lhld arg2 ;stick first parameter string pointer mvi b,0 ;char count for com. line buf. excl1: push h ;and construct command line mov a,m ;get addr of next arg string pointer inx h mov h,m mov l,a ;0000 indicates end of list. ora h ;end of list? reloc jz,excl3 mvi a,' ' ;no. install next string dcx h excl2: reloc call,mpuc ;convert to upper case for command line buffer stax d inx d inr b inx h mov a,m ora a ;end of string? reloc jnz,excl2 pop h ;yes. inx h ;bump param pointer inx h reloc jmp,ex? lhld base+6 reloc jz,go ;if so, use regular bdos addr lhld tpa+1 ;else get special SP value go: sphl lxi h,base push h ;set base of ram as return addr jmp tpa-42 ;(go to `code0:') ; ; This loader code is now: 42 bytes long. ; code0: lxi d,tpa ;destination address of new program code1: push d ;push dma addr push b ;push fcb pointer mvi c,sdma ;set DMA address for new sector call bdos pop d ;get pointer to working fcb in DE push d ;and re-push it mvi c,reads ;read a sector call bdos pop b ;restore fcb pointer into BC pop d ;and dma address into DE ora a ;end of file? jz tpa-8 ;if not, get next sector (goto `code2:') mvi c,sdma ;reset DMA pointer lxi d,tbuff call bdos jmp tpa ;and go invoke the program code2: lxi h,80h ; bump dma address dad d xchg jmp tpa-39 ;and go loop (at code1) postlude execl ; ; Sbrk: ; ; sbrk(n) ; ; Allocate n bytes of ram and return a pointer to the base of the ; allocated area. Updator mov a,h ;negate cma mov h,a mov a,l cma mov l,a inx h dad sp ;get HL = (SP - alocmx) reloc call,cmpdh ;is DE less than HL? reloc jnc,brkerr ;if not, can't provide the needed memory. xchg ;else OK. inx h shld allocp ;save start of next area to be allocated pop h ;get pointer to this area ret ;and return with it. brkerr: pop h ;clean up stack jmp error ;and return with -1 to indicate can't allocate. cmpdh: mov a,d cmp h rc rnz mov a,e cmp l re; ; More assembly-language BDS C library functions ; This file contains the DMA-video and CP/M 2.0 random-access ; file I/O routines. ; maclib bds maclib cmac direct define setplot ;define video board dimensions define clrplot ;clear the screen define line ;draw a crooked line define plot ;plot a point define txtplot ;put text up define rread define rwrite define rseek define rtell define rsrec define rcfsiz enddir ; ; Setplot: ; ; Set up plotting paraDE lhld pbase ;get screen base in HL clr2: mvi m,' ' ;and inx h ; clear dcx d ; each mov a,d ; location ora e ; (all DE of 'em) reloc jnz,clr2 ret postlude clrplot ; ; Line: ; ; This function draws a line between any 2 points on a device ; such as the VDM-1. ; ; The address of the VDM-1 screen is stored at pbase. ; This routine will only work for 16x64-orgranized video ; displays. ; prelude line call arghak ;get args push b lda arg2 ;put one set oon the screen): ; liner: mov a,d sub h reloc call,abs cpi 2 reloc jnc,line2 ;are points far enough apart ;in both dimensions to warrant mov a,e ;drawing a line? sub l reloc call,abs cpi 2 reloc jnc,line2 ret ;if not, return. ; ; Yes, the two points are sufficiently far apart to warrant a ; line. So, draw it: ; line2: reloc call,midp ;find midpoint reloc call,put ;put it up push d ;set up recursive call mov d,b mov e,c ; ; Draw a line between one orte allocp by n, and make sure that the the new ; value of allocp is less than (SP - alocmx). If it isn't, return -1. If it ; is, just return the address of the allocated area. ; prelude sbrk call ma1toh ;get # of bytes needed in HL xchg ;put into DE lhld allocp ;get current allocation pointer push h ;save it dad d ;get tentative last address of new segment reloc jc,brkerr ;better not allow it to go over the top! dcx h xchg ; now last addr is in DE lhld alocmx ;get safety fact postlude sbrk ; ; Rsvstk: ; ; rsvstk(nbytes) ; ; Makes sure that "sbrk" never allocates storage closer than "nbytes" ; bytes to the current stack top. ; prelude rsvstk call ma1toh ;get the value to reserve shld alocmx ;and set new safety factor ret postlude rsvstk end ;end of this file up stack jmp error ;and return with -1 to indicate can't allocate. cmpdh: mov a,d cmp h rc rnz mov a,e cmp l remters for the DMA video plotting ; routines: ; prelude setplot call arghak push b lhld arg1 ;get base address shld pbase ; initialize lhld arg3 ;get y size shld ysize ; initialize xchg ;leave it in DE lhld arg2 ;get x size shld xsize ; initialize call usmul ;figure out screen size shld psize ; initialize pop b ret postlude setplot ; ; Clrplot: ; ; Clear screen on DMA plotting device: ; prelude clrplot lhld psize ;put screen size xchg ; in f endpoint data in DE in mov c,a ;format: D = x = arg2, E = y = arg3 lda arg3 mov b,a mov d,b mov e,c reloc call,put ; put up one endpoint at BC lda arg4 ;put other endpoint data in HL mov c,a lda arg5 mov b,a reloc call,put ;(but first put up the point from BC) mov h,b mov l,c reloc call,liner ;now connect them... pop b ret ;all done. ; ; This routine draws a line between points specified by DE ; and HL (it assumes that the two endpoints are already up ; iginal point and the midpoint: ; reloc call,liner xthl ; ; Draw a line between the other original point and the midpoint: ; (Isn't recursion fun?) ; reloc call,liner xchg pop h ret ;and we are done! ; ; This routine finds the point as close as possible to the ; midpoint between the two points specified in HL and DE. ; Coordinates of the midpoint are returned in BC: ; midp: push h push d mov a,h sub d ani 1 reloc jz,mid3 mov a,h cmp d reloc jc,mid2a inr d reloc jmp,mid3 mid2a: dcr h mid3: mov a,l sub e ani 1 reloc jz,mid4 mov a,l cmp e reloc jc,mid3a inr e reloc jmp,mid4 mid3a: dcr l mid4: mov a,h add d ora a rrc mov b,a mov a,l add e ora a rrc mov c,a pop d pop h ret ; ; Subroutine to put a point on the screen at the location ; specified by BC, where B=y, C=x: ; put: push b push h lhld pbase mov a,c rrc rrc push psw ani 0c0h mov l,a pop psw ani 3 add h mov h,a mov text up on the DMA device: ; prelude txtplot call arghak push b lhld arg2 xchg lhld ysize call usmul xchg lhld arg3 dad d xchg lhld pbase dad d xchg lhld arg1 mvi b,0 lda arg4 ora a reloc jz,txt2 mvi b,80h txt2: mov a,m ora a reloc jnz,txt3 pop b ret txt3: ora b stax d inx h inx d reloc jmp,txt2 postlude txtplot ; ; Here are the new random-access file I/O routines ; for use with CP/M version 2.x...but not if you ; expect to uo go back to a previous ; sector. ; prelude rread call arghak lda arg1 call fgfd jc error mov a,m ani 2 jz error push b lda arg1 call fgfcb shld tmp2 lxi h,0 shld tmp2a r2: lhld arg3 mov a,h ora l lhld tmp2a reloc jnz,r2a pop b ret r2a: lhld arg2 xchg mvi c,sdma call bdos lhld tmp2 xchg mvi c,readr ;code for BDOS random read push d ;save de so we can fudge nr field if call bdos ;we stop reading on extent boundary... pop d ; CP/M sucks! d d shld arg2 lhld tmp2a inx h shld tmp2a lhld tmp2 ;get address of fcb lxi b,33 ;get addr of random record field dad b mov c,m ;bump inx h ; value mov b,m ; of inx b ; random mov m,b ; field dcx h ; by one mov m,c reloc jmp,r2 postlude rread ; ; Rwrite: ; ; The random "write" routine, which always copies the sector ; to be written down to tbuff before writing. Returns ; the # of sectors successfully written, or -1 on hard error. ; (the "1000 b,128 nwr3: mov a,m stax d inx h inx d dcr b reloc jnz,nwr3 shld arg2 ;save -> to next 128 bytes lhld tmp2 ;get addr of fcb xchg mvi c,writr ;go write randomly call bdos ora a ;error? lhld tmp2a ;if so, return # of successfully written pop b ; sectors. rnz push b inx h ; else bump successful sector count, shld tmp2a lhld arg3 ; debump countdown, dcx h shld arg3 lhld tmp2 ; get address of fcb lxi b,33 ; get address of random field dad b mov c,m ; bumc,b mvi b,0 dad b lda arg1 mov m,a pop h pop b ret ; ; Subroutine to return A=abs(A): ; abs: ora a rp cma inr a ret postlude line ;end of "line" function ; ; Plot: ; ; Put a point on the DMA device: ; prelude plot call arghak lda arg1 lhld ysize xchg lhld pbase inr a plot1: dcr a reloc jz,plotc dad d reloc jmp,plot1 plotc: lda arg2 mov e,a mvi d,0 dad d lda arg3 mov m,a ret postlude plot ; ; Txtplot: ; ; Put somese the generated programs on CP/M 1.4. ; ; the new functions are: rread, rwrite, rtell, rseek, ; rsrec, rcfsiz ; ; ; Rread: ; ; Read a number of sectors randomly ; Usage: ; ; i = rread(fd, buf, n); ; ; The return value is either the number of sectors successfully ; read, 0 for EOF, or 1000 + (BDOS ERROR CODE) ; ; The Random Record Field is incremented following each successful ; sector is read, just as if the normal (sequentail) read function ; were being used. Rseek must be used t ora a reloc jz,r4 ;go to r4 if no problem cpi 1 reloc jz,r2b ;EOF? mov c,a ;put return error code in BC mvi b,0 lxi h,1000 ;add to 1000 dad b pop b ret r2b: lxi h,32 ;yes. are we on extent boundary? dad d mov a,m cpi 80h reloc jnz,r3 mvi m,0 ;yes. reset nr to 0...CP/M leaves it at 80! r3: lhld tmp2a ;(note: the above "bug" in CP/M was supposedly fixed pop b ; for 2.x, but one can never be sure...) ret r4: lhld arg3 dcx h shld arg3 lhld arg2 lxi d,128 da + error code" business is not used for rwrite) ; prelude rwrite call arghak lda arg1 call fgfd jc error mov a,m ani 4 jz error push b lda arg1 call fgfcb shld tmp2 lxi h,0 shld tmp2a lxi d,tbuff ;80 for normal CP/M, else 4280 mvi c,sdma call bdos nwr2: lhld arg3 ;done yet? mov a,h ora l lhld tmp2a ;if so, return count reloc jnz,nwr2a pop b ret nwr2a: lhld arg2 ;else copy next 128 bytes down to tbuff lxi d,tbuff ;80 for normal CP/M, else 4280 mvip 16-bit value at random inx h ; record mov b,m ; field inx b ; of mov m,b ; fcb dcx h ; by one mov m,c reloc jmp,nwr2 ; and go try next sector postlude rwrite ; ; rseek: ; ; rseek(fd, offset, origin) ; seeks to offset records if origin == 0, ; to present position + offset if origin == 1, ; or to end of file + offset if origin == 2. ; (note that in the last case, the offset must be non-positive) ; prelude rseek call arghak lda arg1 call fgfcb jc error push h reloc call,rtell2 lhld arg2 lda arg3 ;is origin == 0? ora a reloc jz,rseek2 ;if so, HL holds new position dcr a ;no. is origin == 1? reloc jnz,rseek1 dad d ;yes. add offset to current position reloc jmp,rseek2 ;and result is in HL rseek1: pop d ;else origin must be 2... push d push b mvi c,cfsizc ;compute end of file position call bdos pop b pop h ;get back fcb push h reloc call,rtell2 ;get DE = position lhld arg2 ;add offset dad d ;and e,m ;get value into DE inx h mov d,m xchg ;put into HL ret postlude rtell ; ; Rsrec: ; ; Set random field from serial access mode: ; prelude rsrec call arghak lda arg1 call fgfcb jc error push h xchg push b mvi c,srrecc call bdos pop b pop h lxi d,33 dad d mov a,m inx h mov h,m mov l,a ret postlude rsrec ; ; Rcfsiz: ; ; set random record field to end-of-file: ; prelude rcfsiz call arghak lda arg1 call fgfcb jc error The Incredible Superpowerful Floating Point Package for BDS C v1.4 ************************************* software written by: Bob Mathias this documentation by: Leor Zolman Components of the floating point package: 1) FLOAT.DOC: This documentation file 2) FLOAT.C: File of support functions, written in C 3) FP: The workhorse function (in DEFF2.CRL) 4) FLOATSUM.C A Sample use of all this stuff This floating point package is as close as BDS C veo specify it in a function call. Each of Bob's functions expects its arguments to be pointers to such character arrays. The four basic arithmetic functions are: fpadd, fpsub, fpmul and fpdiv. They each take three arguments: a pointer to a five character array where the result will go, and the two operands (each a pointer to a five character array representing a floating point operand.) To facilitate the initialization of the floating point character arrays with the values you desire and prin------------------- The following functions allow BDS C compiler users to access and manipulate real numbers. Each real number must be allocated a five (5) byte character array (char fpno[5]). The first four bytes contain the mantissa with the first byte being the least significant byte. The fifth byte is the exponent. fpcomp(op1,op2) char op1[5],op2[5]; Returns: an integer 1 if op1 > op2 an integer -1 if op1 < op2 a zero if op1 = op2 As with most floating point packages, it HL holds new position rseek2: xthl ;get fcb, push new position lxi d,33 dad d ;HL points to random field of fcb pop d ;get new position in DE mov m,e ;and put into fcb inx h mov m,d xchg ;and return the position value ret rtell2: lxi d,33 dad d mov e,m inx h mov d,m ret postlude rseek ; ; Rtell: ; ; Return random record position of file: ; prelude rtell call arghak lda arg1 call fgfcb jc error lxi d,33 ;go to random record field dad d mov push h xchg push b mvi c,cfsizc call bdos pop b pop h lxi d,33 dad d mov a,m inx h mov h,m mov l,a ret postlude rcfsiz c,srrecc call bdos pop b pop h lxi d,33 dad d mov a,m inx h mov h,m mov l,a ret postlude rsrec ; ; Rcfsiz: ; ; set random record field to end-of-file: ; prelude rcfsiz call arghak lda arg1 call fgfcb jc error rsion 1.x is ever gonna come to manipulating floating point numbers. And it ain't too bad, actually...Bob did a nice neat job, and the new formatted printout support in a special version of the "_spr" library function (source is in FLOAT.C) means that floating point output is no longer limited to scientific notation. Here's how it works: for every floating point number you wish to work with, you must declare a five (5) element character array. Then, pass a pointer to the array whenever you need tting out the values in a human-readable form, the following functions are included: ftoa: converts a floating point number to an ASCII string (which you can then print out with "puts") NOTE: explicit use of this function has been made obsolete by the new "sprintf." See FLOAT.C. atof: converts an ASCII string (null terminated) to a floating point number itof: converts integer to floating point. Here are Bob's descriptions of the functions: ----------------is not a good practice to compare for equality when dealing with floating point numbers. char *fpadd(result,op1,op2) char result[5], op1[5], op2[5]; Stores the result of op1 + op2 in result. op1 and op2 must be floating point numbers. Returns a pointer to the beginning of result. char *fpsub(result,op1,op2) char result[5],op1[5],op2[5]; Stores the result of op1 - op2 in result. op1 and op2 must be floating point numbers. Returns a pointer to the beginning of result. char *fpmult(result,op1,op2) char result[5],op1[5],op2[5]; Stores the result of op1 * op2 in result. op1 and op2 must be floating point numbers. Returns a pointer to the beginning of result. char *fpdiv(result,op1,op2) char result[5],op1[5],op2[5]; Stores the result of op1 / op2 in result. op1 and op2 must be floating point numbers. A divide by zero will return zero as result. Returns a pointer to the beginning of result. char *atof(op1,s1) char op1[5],*s; Converts the AS,op1) char *s1,op1[5]; Converts the floating point number op1 to an ASCII string. It will be formatted in scientific notation with seven (7) digits of precision. The string will be terminated by a null. Returns a pointer to the beginning of s1. char *itof(op1, n) char op1[5]; int n; Sets the floating pt. number op1 to the value of integer n. n is assumed to be a SIGNED integer. General observations: Because floating point operations must be thought of in terms ong: char fpno[5]; int ival; ... fpno = ival; will not work; you have to change that last line to: itof(fpno,ival); Some more examples: The following will add 100.2 & -7.99 and store the result at the five character array location 'a': fpadd(a,atof(b,"100.2"), atof(c,"-7.99")); (note that "b" and "c" must also be five character arrays) The following would NOT add 1 to 'a' as both op1 and op2 must be floating point numbers (actually pointers to characters...): f147 Los Alisos apt. 268 Mission Viejo, Ca. 92691 At Bob's request, the source for the "fp" function has not been included in the package and is not generally available. If you really need the thing, try contacting Bob at the above address and ask for it. machine-coded part of the package. Questions on the internals of this package should be addressed to: Bob Mathias 23 /* Floating point package support routines Note the "fp" library function, available in DEFF2.CRL, is used extensively by all the floating point number crunching functions. (see FLOAT.DOC for details...) NEW FEATURE: a special "printf" function has been included in this source file for use with floating point operands, in addition to the normal types. The printf presented here will take precedence over the DEFF.CRL version when "float" is specified CII string s1 into a floating point number and stores the result in op1. The function will ignore leading white space but NO white space is allowed to be embedded withing the number. The following are legal examples: "2", "22022222222383.333", "2.71828e-9", "334.3333E32". "3443.33 E10" would be ILLEGAL because it contains an embedded space. The value of the exponent must be within the range: -38 <= exponent <= 38. A pointer to the result is returned. char *ftoa(s1f FUNCTION CALLS rather than simple in-line expressions, special care must be taken not to confuse the abilities of the compiler with the abilities of the floating point package. To give a floating point number an initail value, for instance, you cannot say: char fpno[5]; fpno = "2.236"; To achieve the desired result, you'd have to say: char fpno[5]; atof(fpno,"2.236"); Moreover, let's say you want to set a floating point number to the value of an integer variable called "ival". Sayipadd(a,a,1); /* bad use of "fpadd" */ Thus, it can get a bit hairy when all floating point numbers are really character arrays; but still, it's better than nothing. All of the above functions are written in C, but most of them call a single workhorse function called "fp" to do all the really hairy work. This function has been placed into the DEFF2.CRL; it is the only machine-coded part of the package. Questions on the internals of this package should be addressed to: Bob Mathias 23 on the CLINK command line at linkage time. Note that the "fp" function, needed by most of the functions in this file, resides in DEFF2.CRL and will be automatically collected by CLINK. All functions here written by Bob Mathias, except printf and _spr (written by Leor Zolman.) */ #include "bdscio.h" #define NORM_CODE 0 #define ADD_CODE 1 #define SUB_CODE 2 #define MULT_CODE 3 #define DIV_CODE 4 #define FTOA_CODE 5 fpcomp(op1,op2) char *op1,*op2; { char work[5]; fpsub(work,op1,op2); if (work[3] > 127) return (-1); if (work[0]+work[1]+work[2]+work[3]) return (1); return (0); } fpnorm(op1) char *op1; { fp(NORM_CODE,op1,op1);return(op1);} fpadd(result,op1,op2) char *result,*op1,*op2; { fp(ADD_CODE,result,op1,op2);return(result);} fpsub(result,op2,op1) char *result,*op1,*op2; {fp(SUB_CODE,result,op1,op2);return(result);} fpmult(result,op1,op2) char *result,*op1,*op2; { fp(MULT_CODE,result,op1,op2);return(result);} fpdiv(result,o for (;isdigit(*s);--power,++s){ fpmult(fpno,fpno,FP_10); work[0]=*s-'0'; work[1]=work[2]=work[3]=0;work[4]=31; fpadd(fpno,fpno,fpnorm(work)); } } if (toupper(*s) == 'E') {++s; power += atoi(s); } if (power>0) for (;power!=0;--power) fpmult(fpno,fpno,FP_10); else if (power<0) for (;power!=0;++power) fpdiv(fpno,fpno,FP_10); if (sign_boolean){ setmem(ZERO,5,0); fpsub(fpno,ZERO,fpno); } return(fpno); } ftoa(result,op1) char *result,*op1; { fp(FTOA_CODE,resa function is not linked in UNTIL IT IS REFERENCED...so if the definition of "printf" were not placed here in this file, "_spr" would not be referenced at all until the "printf" from DEFF.CRL got yanked in, at which time "_spr" would ALSO be taken from DEFF.CRL and cause the floating point "_spr" options to not be recognized. In other words, if "printf" were not given explicitly here, the WRONG _spr would end up being used. */ printf(format) char *format; { char line[MAXLINE]; _sspecial printf/sprintf combination; to achieve the same result as ftoa, a simple "%e" format conversion will do the trick. "%f" is used to eliminate the scientific notation and set the precision. The only [known] difference between the "e" and "f" conversions as used here and the ones described in the Kernighan & Ritchie book is that ROUNDING does not take place in this version...e.g., printing a floating point number which happens to equal exactly 3.999 using a "%5.2f" format conversion wilif ( !(width = _gv2(&format))) width++; if ((c = *format++) == '.') { precision = _gv2(&format); pf++; c = *format++; } switch(toupper(c)) { case 'E': if (precision>7) precision = 7; ftoa(wbuf,*args++); strcpy(wbuf+precision+3, wbuf+10); width -= strlen(wbuf); goto pad2; case 'F': ftoa(&wbuf[60],*args++); sptr = &wbuf[60]; while ( *sptr++ != 'E') ; exp = atoi(sptr); sptr = &wbuf[60]; if p1,op2) char *result,*op1,*op2; { fp(DIV_CODE,result,op1,op2);return(result);} atof(fpno,s) char fpno[5],*s; { char *fpnorm(),work[5],ZERO[5],FP_10[5]; int sign_boolean,power; initb(FP_10,"0,0,0,80,4"); setmem(fpno,5,0); sign_boolean=power=0; while (*s==' ' || *s=='\t') ++s; if (*s=='-'){sign_boolean=1;++s;} for (;isdigit(*s);++s){ fpmult(fpno,fpno,FP_10); work[0]=*s-'0'; work[1]=work[2]=work[3]=0;work[4]=31; fpadd(fpno,fpno,fpnorm(work)); } if (*s=='.'){ ++s;ult,op1);return(result);} itof(op1,n) char *op1; int n; { char temp[20]; return atof(op1, itoa(temp,n)); } itoa(str,n) char *str; { char *sptr; sptr = str; if (n<0) { *sptr++ = '-'; n = -n; } _uspr(&sptr, n, 10); *sptr = '\0'; return str; } /* The short "printf" function given here is exactly the same as the one in the library, but it needs to be placed here so that the special "_spr" is used instead of the normal one in DEFF.CRL. The way the linker works is that pr(line,&format); /* use "_spr" to form the output */ puts(line); /* and print out the line */ } /* This is the special formatting function, which supports the "e" and "f" conversions as well as the normal "d", "s", etc. When using "e" or "f" format, the corresponding argument in the argument list should be a pointer to one of the five-byte strings used as floating point numbers by the floating point functions. Note that you don't need to ever use the "ftoa" function when using this l produce " 3.99" instead of " 4.00". */ _spr(line,fmt) char *line, **fmt; { char _uspr(), c, base, *sptr, *format; char wbuf[80], *wptr, pf, ljflag; int width, precision, exp, *args; format = *fmt++; /* fmt first points to the format string */ args = fmt; /* now fmt points to the first arg value */ while (c = *format++) if (c == '%') { wptr = wbuf; precision = 6; ljflag = pf = 0; if (*format == '-') { format++; ljflag++; } (*sptr == ' ') sptr++; if (*sptr == '-') { *wptr++ = '-'; sptr++; width--; } sptr += 2; if (exp < 1) { *wptr++ = '0'; width--; } pf = 7; while (exp > 0 && pf) { *wptr++ = *sptr++; pf--; exp--; width--; } while (exp > 0) { *wptr++ = '0'; exp--; width--; } *wptr++ = '.'; width--; while (exp < 0 && precision) { *wptr++ = '0'; exp++; precision--; width--; } while (precision && pf) { *wptr++ = *sptr++; pf--; precision--; width--; } while (precision>0) { *wptr++ = '0'; precision--; width--; } goto pad; case 'D': if (*args < 0) { *wptr++ = '-'; *args = -*args; width--; } case 'U': base = 10; goto val; case 'X': base = 16; goto val; case 'O': base = 8; val: width -= _uspr(&wptr,*args++,base); lt: *line++ = c; } } else *line++ = c; *line = '\0'; }  sptr = *args++; while (*sptr && precision) { *wptr++ = *sptr++; precision--; width--; } pad: *wptr = '\0'; pad2: wptr = wbuf; if (!ljflag) while (width-- > 0) *line++ = ' '; while (*line = *wptr++) line++; if (ljflag) while (width-- > 0) *line++ = ' '; break; defau /* This program is a simple example of how to use Bob Mathias's floating point package. To compile it, first compile (but do not link) both this file and FLOAT.C. Then, give the CLINK command: A>clink floatsum float and run the thing by saying: A>floatsum Note: the "printf" function resulting from this linkage will support the "e" and "f" floating point conversion characters, but the regular "printf" would not. The reason: the special version of "_spr" in the FLOAT.C goto pad; case 'C': *wptr++ = *args++; width--; goto pad; case 'S': if (!pf) precision = 200; sptr = *args++; while (*sptr && precision) { *wptr++ = *sptr++; precision--; width--; } pad: *wptr = '\0'; pad2: wptr = wbuf; if (!ljflag) while (width-- > 0) *line++ = ' '; while (*line = *wptr++) line++; if (ljflag) while (width-- > 0) *line++ = ' '; break; defausource file takes precedence over the library version of "_spr", and thus supports the extra features. See the comments in FLOAT.C for more details on this strangeness. */ main() { char s1[5], s2[5], s3[5]; char string[30]; char sb[30]; int i; atof(s1,"0"); while (1) { printf("sum = %10.6f\n",s1); printf("\nEnter a floating number: "); fpadd(s3,s1,atof(s2,gets(string))); for (i=0; i<5; i++) s1[i] = s3[i]; } }