Digital Research
This commit is contained in:
2020-11-06 18:50:37 +01:00
parent 621ed8ccaf
commit 31738079c4
8481 changed files with 1888323 additions and 0 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,116 @@
:10F00000C35BF0C346F6C356F6C300F6C37CF6C333
:10F0100010F6C323F6C36AF1C365F1C38AF0C39443
:10F02000F6C394F6C3CFF3F8F009F109F1ACF1F6A9
:10F03000F43CF1FDF1D0F54DF209F109F10EF55D69
:10F04000F209F155F209F121F54CF567F28FF20959
:10F05000F191F18DF5ECF29FF482F1F3313F002153
:10F0600000C311B2F60610D5E510FC3195F03E0054
:10F06F00C50100F021FFFF247E2F77BE2F772004EC
:10F07F007CB820F32501DEFF09C1C9E5CD6FF07D16
:10F08F00D63C30012544E1C999F0F91145F4EB0163
:10F09F001D00EDB0010600D5E12BEDB021E8FF39E1
:10F0AF00E52323220600160AC51520FCCD94F6CDC4
:10F0BF009FF4CD94F62190F4CD95F61843060121D7
:10F0CF000000180C1079203205C8210000CD7BF309
:10F0DF004FCDB0F3380829292929B56F18EFE3E58B
:10F0EF0079CDC3F330E01012C9CD7BF3216EF1114E
:10F0FF0005000604BE28421910FA218CF4CD98F6AB
:10F10F002A0600F9210FF1E52201003EC33200006B
:10F11F00CDA9F6CD78F3D64138E0FE1A30DC875F03
:10F12F00160006022127F0197E23666FE9CD86F3BC
:10F13F0071CD8FF330FAD118C7500604CD78F32371
:10F14F00BE2081682D422603052804292910FC3A88
:10F15F000300B4ACB54F79320300C93A0300C94C70
:10F16F0032314C5450323150545232315054433169
:10F17F004243540602CD8FF6FE0720F7CD7EF310E3
:10F18F00F4C9CD86F30AC546B8280CF5CDFBF57842
:10F19F00CDF4F5F1CDE6F5C1CD9BF318E8CDA4F68E
:10F1AF00CDFBF57DCDF0F1E57ECDE6F5CD8FF338D6
:10F1BF002ACDFEF57DE60F20EFE17DE60FCDF5F1CF
:10F1CF007EE67F4FFE203804FE7E38020E2ECD09DC
:10F1DF00F0CD9CF37DE60F20E718C593CDF0F11825
:10F1EF00D8E60F4787804704CDFEF510FBC9CDC089
:10F1FF00F338372810CDCCF0D121340039722B736E
:10F20F0079FE0D2825060221350039C5E50602CD08
:10F21F00D7F0D1E17AB3280A732372231A77233EEA
:10F22F00CF1279FE0DC1280210E1CDA9F6E12143DD
:10F23F00F4E521CFF322090021180039D1E9CDD708
:10F24F00F0C1ED581851CDD9F0D1C1ED59C9CD86C6
:10F25F00F37E02CD9BF318F9CDD7F0E17ECDF4F517
:10F26F00CDC0F3D8280FFE0A280DE5CDCCF0D1E1A3
:10F27F007379FE0DC823232B7DE607CCFBF518DC35
:10F28F00CDA4F67EF52F77AEC4A1F2F177CD9CF326
:10F29F0018F1D55FCDFBF506087B075F3E18174FBA
:10F2AF00CD09F010F4D1C9232334C8F2C1F2F6808E
:10F2BF001802E67F35BE20EFCDFEF5CD15F3CDF765
:10F2CF00F5CDC0F3D82812E5CDCCF0E17D1312E3D4
:10F2DF007EE3073003137C12E179FE0DC8213DF365
:10F2EF00CDC0F3380B28F9FE2720BE2155F318F0B7
:10F2FF007E4F3CC8FCA9F6CD09F0CDF7F5CD15F33F
:10F30F00CDFEF52318EA237EE63FC602EB6F2600FB
:10F31F0039EB7E060107300E0407300AE51A671B2A
:10F32F001A6F7EE110011ACDE6F51B10F9C9C11550
:10F33F004213431244114510461448314C30CDF15D
:10F34F0050B453974903C109420B430A440D450C6E
:10F35F004608480F4C0ECDCF588759855202FFE60D
:10F36F000FC69027CE40274FC9CDF7F5CD8FF6C5E5
:10F37F004FCD09F079C1C904CDD9F0C1D1C3AAF6D7
:10F38F00237CB537C87B957A9CC9D1C903CD8FF340
:10F39F0038F8CD12F0B7C8CD8FF6FE1320ECC38F1F
:10F3AF00F6D630D8FE173FD8FE0A3FD0D607FE0A52
:10F3BF00C9CD7BF3FE20C8FE2CC8FE0D37C83FC950
:10F3CF00E5D5C5F5CD6FF0EB210A00390604EB2B1F
:10F3DF00722B73D110F9C10BF921250039D5160203
:10F3EF007E91237E98280623231520F403212000E5
:10F3FF00D139732372C50E2ACD09F0D13EF4BA2844
:10F40F00092323732372EBCDE1F521250039010088
:10F41F00025E71235671237BB228027E122310F1F4
:10F42F0008D9E5D5C5F5DDE5FDE5ED5747ED5F4FAE
:10F43F00C5C313F1E5CFC179ED4F78ED47DDE1FDA0
:10F44F00E1F1C1D1E108D9D1C1F1E1F90021000009
:10F45F00C30000AF320300216CF4C3B5F6492F4F40
:10F46F00204552D2CDE8F6B047825778C90E0DCD60
:10F47F007CF60E0AC37CF6CD56F6E67FC93F3F3FBA
:10F48F00BF4D4F5353205645525320322E320D8AC3
:10F49F003E0FD324114000626ADB26A328FBDB2634
:10F4AF0023A3A3C2ADF4E5295C1919E52929DB20B3
:10F4BF002B7DB4C2BDF4E13E83D3237DD3207CD317
:10F4CF00213E03D323AFD321D325CDCEF6E67FFE46
:10F4DF000DE1C85D54CDEEF4CDEEF419E518D8B7B3
:10F4EF007C1F677D1F6FC9CDA4F6D5CDC8F5AF576B
:10F4FF00CDF6F63E01CDFEF6AF92CDFEF61803CD5A
:10F50F00D7F0C178B1410E002002063CCD0CF010AF
:10F51F00FBC93A03000604217DF111FBFFF5CDFE77
:10F52F00F54ECD09F0CDF7F5F1F5E5233CE60320D7
:10F53F00FA4ECD09F0E1F11F1F1910E1C9CDD7F037
:10F54F00E1E5CD86F4DE3A20F957CD73F428235F39
:10F55F00CD73F4F5CD73F4C14F09CD73F4CD73F4BE
:10F56F00772FAEC4A1F2231D20F3CD73F4C209F19E
:10F57F0018CECD73F467CD73F46FB4D1C8E9CD86CF
:10F58F00F3AF47B120020E10E509B7ED52E1380A8B
:10F59F00D5EBB7ED5223E3EBC1D8C5D55041CDC85C
:10F5AF00F578CDF6F6AFCDFEF67E23CDFEF610F94B
:10F5BF00AF92CDFEF6D1C118CFCD7CF40E3AC37CFD
:10F5CF00F6CDA4F6E519CDFBF5E1B7ED521803CD55
:10F5DF00A9F67CCDE6F57DF50F0F0F0FCDEFF5F109
:10F5EF00CD6EF3180CCDE6F50E2D1805CDDEF50E0C
:10F5FF00203A0300E603CADEF6FE02FA62F4C262A4
:10F60F00F43A0300E6C0CADEF6FE80FA62F4CA627C
:10F61F00F4C362F43A0300E603CAC6F6FE02FA62C6
:10F62F00F4C262F43A0300E60CCAC6F6FE08FA62A8
:10F63F00F4CA62F4C362F43A0300E603CACEF6FEDC
:10F64F0002FA62F4C262F43A0300E60CCACEF6FE86
:10F65F0008FA62F4CA62F4C362F43A0300E6C0CA5D
:10F66F00D6F6FE80FA62F4CA62F4C362F43A03007B
:10F67F00E630CADEF6FE20FA62F4CA62F4C362F420
:10F68F00CD46F6E67FC9CDA9F6C54ECD00F6237956
:10F69F000730F7C1C9CDD9F0D1E1E521C2F6CD9838
:10F6AF00F6E1C921BBF6CD95F6C3000052535420A5
:10F6BF004552D20D0A0080DB25E601C8C6FEC9DB24
:10F6CF00251F30FBDB20C9DB25E620C8C6BFC9DB01
:10F6DF0025E62028FA79D320C9CDF0F607070707CA
:10F6EF0047CD86F4C3B0F3CDFEF67CCDFEF67DF5A7
:10F6FF000F0F0F0FCD6EF3CD0CF0F1F5CD6EF3CDE7
:06F70F000CF0F18257C965
:0000000000


File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
F17D ACT F33D ACTBL 0015 ALOC F16E ALT 0009 APLOC
F173 APT F178 ART F103 AS0 F148 AS1 F14E AS2
F0D3 AS3 F15A AS4 F15E AS5 F0F8 ASGN F633 BATST
0000 BC 0007 BELL F2A8 BITS1 F2A6 BITS2 F2A1 BITS
F5FE BLK 0013 BLOC 000B BPLOC F182 BYE F184 BYE1
F473 BYTE F6E8 BYT F000 CBOOT F646 CI 0012 CLOC
F194 CMPA F1A6 CMPB F6B5 COMERR F191 COMP F68F CONI
F003 CONIN F009 CONOUT F600 CO F012 CONST F36E CONV
000A CPLOC 000D CR F6A9 CRLF F6AA CRLFA F6C2 CRMSG
F462 CRTIN F462 CRTOST F462 CRTOUT F462 CRTST F623 CSTS
0013 CTRLS F462 CUSI1 F462 CUSO1 F462 CUST1 F5F4 DASH1
F5F7 DASH 0002 DE F378 DECHO F1AF DIS1 F1B7 DIS2
F1C8 DIS3 F1CF DIS4 F1DB DIS5 F1DD DIS6 F1EA DIS7
F1AC DISP F4EE DIV2 0011 DLOC 000D DPLOC F37E ECH1
F37B ECHO 0010 ELOC F462 ENDX F4F6 EOF F4FA EOFA
000C EPLOC F0DC EX0 F0DF EX1 F0ED EX2 F0D5 EX3
F0CC EXF F445 EXIT F6A4 EXLF F0D7 EXPR1 F0D9 EXPR
F386 EXPR3 F097 FAKE F13C FILL F13F FIO 0014 FLOC
000C FMFD 0008 FPLOC F214 GO0 F21A GO1 F231 GO2
F239 GO3 F1FD GOTO F5E6 HEX1 F5EF HEX2 F5D0 HEXN
F38F HILO F399 HILOD F39B HILOXB F39C HILOX 0004 HL
0031 HLOC 000F HPLOC F462 HSP F462 HSPST F49F I8250
F4A8 I8250A F4AD I8250B F4BD I8250C F4C6 I8250D 0003 ILOC
F05B INIT F067 INIT1 F0B7 INIT2 F24D INPT 0003 IOBYTE
F16A IOCHK F462 IOER F46C IOMSG F165 IOSET 0004 IX
0004 IY F5DE LADRA F5FB LADRB F5E1 LADR F51B LE1
F50E LEADER F511 LEO 000A LF F00F LIST 0030 LLOC
0020 LLOCX F610 LO F490 LOGMSG 000E LPLOC F462 LPRST
F462 LPRT F669 LSTAT F462 LUSE1 F462 LUST1 F08A MEMCK
F094 MEMCK0 F06F MEMSIZ F076 MEMSZ1 F083 MEMSZ2 F000 MOSS
F260 MOV1 F25D MOVE F28F MTEST F292 MTEST1 F29A MTEST2
0002 NBKPTS F3B0 NIBBLE F255 OUPT F3C3 P2C F6F9 PADR
F6F6 PBADR F6FE PBYTE F3C0 PCHK F47C PEOL 0034 PLOC
F67C PO F355 PRMTB F699 PRTA F6A2 PRTB F315 PRTVAL
F698 PRTWA F695 PRTWD F5C8 PSOR F462 PTRIN F462 PTRST
F00C PUNCH F462 PUSO1 F462 PUSO2 F335 PV1 F336 PV2
F48C QMSG F109 QPRT F52C QUE1 F53A QUE2 F521 QUERY
F006 READER F54C READ F551 RED1 F56C RED2 F581 RED3
F54F REDO F3CF REST F656 RI F6F0 RIBBLE F486 RIX
0002 RLOC F000 ROM F3DE RS1 F3EF RS2 F3F6 RS3
F3FB RS4 F3FC RS5 F419 RS6 F420 RS7 F42C RS8
F443 RS9 F6B2 RSTER F6BB RSTMSG F694 RTS F462 RUSI1
F462 RUSI2 F462 RUST1 F462 RUST2 0020 SDATA 0022 SIDENT
0021 SINTEN 0023 SLCTRL 0017 SLOC 0025 SLSTAT 0024 SMDMCT
0026 SMDMST 0006 SPSV F26B SUB1 F284 SUB2 F286 SUB3
F267 SUBS F027 TBL 0035 TLOC 0025 TLOCX F1F7 TRPL1
F1F5 TRPL2 F1F0 TRPLSP F6D6 TTOST F6DE TTPNCH F6C6 TTST
F6CE TTYIN F6DE TTYOUT F6CE TTYRDR F10F WINIT F113 WINITA
F597 WRI1 F5A9 WRI2 F5B8 WRI3 F58D WRITE 0000 WSVEC
F2B6 XAA F2B8 XA F2C1 XAB F2C3 XAC F2E7 XE
F2E8 XF F2FF XG 0007 XLOC F2EC XMNE F2EF XMNE1
0005 YLOC

View File

@@ -0,0 +1,419 @@
Z-80 Macro Library Documentation
--------------------------------
I.
The purpose of this library is to enable the assembly of the Z-80
instruction set on a CP/M sytem using the CP/M MAC macro assembler.
This library is invoked with the pseudo-op:
" MACLIB Z80 "
II.
The following symbols and notations are used in the individual macro
descriptions;
r - Any of the 8 bit registers: A, B, C, D, E, H, L, or M
rr - Any of the 16 bit register pairs: BC, DE, HL, or SP
nn - 8 bit immediate data (0 through 255)
d - 8 bit signed displacment (-128 through +127)
nnnn - 16 bit address or immediate data (0 through 65535)
b - bit number (0-7, 7 is most significant, 0 is least)
addr - 16 bit address within PC+127 through PC-128
m(zzz) - Memory at address "zzz"
III.
MACLIB ver. Zilog ver TDL ver
-------------- ------------- -------------
LDX r,d LD r,(IX+d) MOV r,d(IX)
Load register from indexed memory (with IX)
LDY r,d LD r,(IY+d) MOV r,d(IY)
Load register from indexed memory (with IY)
STX r,d LD (IX+d),r MOV d(IX),r
Store register to indexed memory (with IX)
STY r,d LD (IY+d),r MOV d(IY),r
Store register to indexed memory (with IY)
MVIX nn,d LD (IX+d),nn MVI d(IX)
Move immediate to indexed memory (with IX)
MVIY nn,d LD (IY+d),nn MVI d(IY)
Move immediate to indexed memory (with IY)
LDAI LD A,I LDAI
Move I to A
LDAR LD A,R LDAR
Move R to A
STAI LD I,A STAI
Move A to I
STAR LD R,A STAR
Move A to R
LXIX nnnn LD IX,nnnn LXI IX,nnnn
Load IX immediate (16 bits)
LXIY nnnn LD IY,nnnn LXI IY,nnnn
Load IY immediate (16 bits)
LBCD nnnn LD BC,(nnnn) LBCD nnnn
Load BC direct (from memory at nnnn)
LDED nnnn LD DE,(nnnn) LDED nnnn
Load DE direct
LSPD nnnn LD SP,(nnnn) LSPD nnnn
Load SP direct
LIXD nnnn LD IX,(nnnn) LIXD nnnn
Load IX direct
LIYD nnnn LD IY,(nnnn) LIYD nnnn
Load IY direct
SBCD nnnn LD (nnnn),BC SBCD nnnn
Store BC direct (to memory at nnnn)
SDED nnnn LD (nnnn),DE SDED nnnn
Store DE direct
SSPD nnnn LD (nnnn),SP SSPD nnnn
Store SP direct
SIXD nnnn LD (nnnn),IX SIXD nnnn
Store IX direct
SIYD nnnn LD (nnnn),IY SIYD nnnn
Store IY direct
SPIX LD SP,IX SPIX
Copy IX to the SP
SPIY LD SP,IY SPIY
Copy IY to the SP
PUSHIX PUSH IX PUSH IX
Push IX into the stack
PUSHIY PUSH IY PUSH IY
Push IY into the stack
POPIX POP IX POP IX
Pop IX from the stack
POPIY POP IY POP IY
Pop IY from the stack
EXAF EX AF,AF' EXAF
Exchange AF and the alternate, AF'
EXX EXX EXX
Exchange BC DE HL with BC' DE' HL'
XTIX EX (SP),IX XTIX
Exchange IX with the top of the stack
XTIY EX (SP),IY XTIY
Exchange IY with the top of the stack
LDI LDI LDI
Move m(HL) to m(DE), increment DE and HL, decrement BC
LDIR LDIR LDIR
Repeat LDI until BC = 0
LDD LDD LDD
Move m(HL) to m(DE), decrement HL, DE, and BC
LDDR LDDR LDDR
Repeat LDD until BC = 0
CCI CPI CCI
Compare A with m(HL), increment HL, decrement BC
CCIR CPIR CCIR
Repeat CCI until BC = 0 or A = m(HL)
CCD CPD CCD
Compare A with m(HL), decrement HL and BC
CCDR CPDR CCDR
Repeat CCD until BC = 0 or A = m(HL)
ADDX d ADD (IX+d) ADD d(IX)
Indexed add to A
ADDY d ADD (IY+d) ADD d(IY)
Indexed add to A
ADCX d ADC (IX+d) ADC d(IX)
Indexed add with carry
ADCY d ADC (IY+d) ADC d(IY)
Indexed add with carry
SUBX d SUB (IX+d) SUB d(IX)
Indexed subtract
SUBY d SUB (IY+d) SUB d(IY)
Indexed Subtract
SBCX d SBC (IX+d) SBB d(IX)
Indexed subtract with "borrow"
SBCY d SBC (IY+d) SBB d(IY)
Indexed subtract with borrow
ANDX d AND (IX+d) ANA d(IX)
Indexed logical and
ANDY d AND (IY+d) ANA d(IY)
Indexed logical and
XORX d XOR (IX+d) XRA d(IX)
Indexed logical exclusive or
XORY d XOR (IY+d) XRA d(IY)
Indexed logical exclusive or
ORX d OR (IX+d) ORA d(IX)
Indexed logical or
ORY d OR (IY+d) ORA d(IY)
Indexed logical exclusive or
CMPX d CP (IX+d) CMP d(IX)
Indexed compare
CMPY d CP (IY+d) CMP d(IY)
Index compare
INRX d INC (IX+d) INR d(IX)
Increment memory at m(IX+d)
INRY d INC (IY+d) INR d(IY)
Increment memory at m(IY+d)
DCRX d INC (IX+d) INR d(IX)
Decrement memory at m(IX+d)
DCRY d DEC (IY+d) DCR d(IY)
Decrement memory at m(IX+d)
NEG NEG NEG
Negate A (two's complement)
IM0 IM0 IM0
Set interrupt mode 0
IM1 IM1 IM1
Set interrupt mode 1
IM2 IM2 IM2
Set interrupt mode 2
DADC rr ADC HL,rr DADC rr
Add with carry rr to HL
DSBC rr SBC HL,rr DSBC rr
Subtract with "borrow" rr from HL
DADX rr ADD IX,rr DADX rr
Add rr to IX (rr may be BC, DE, SP, IX)
DADY rr ADD IY,rr DADY rr
Add rr to IY (rr may be BC, DE, SP, IY)
INXIX INC IX INX IX
Increment IX
INXIY INC IY INX IY
Increment IY
DCXIX DEC IX DCX IX
Decrement IX
DCXIY DEC IY DCX IY
Decrement IY
BIT b,r BIT b,r BIT b,r
Test bit b in register r
SETB b,r SET b,r SET b,r
Set bit b in register r
RES b,r RES b,r RES b,r
Reset bit b in register r
BITX b,d BIT b,(IX+d) BIT b,d(IX)
Test bit b in memory at m(IX+d)
BITY b,d BIT b,(IY+d) BIT b,d(IY)
Test bit b in memory at m(IY+d)
SETX b,d SET b,(IX+d) SET b,d(IX)
Set bit b in memory at m(IX+d)
SETY b,d SET b,(IY+d) SET b,d(IY)
Set bit b in memory at m(IY+d)
RESX b,d RES b,(IX+d) RES b,d(IX)
Reset bit b in memory at m(IX+d)
RESY b,d RES b,(IY+d) RES b,d(IY)
Reset bit b in memory at m(IY+d)
JR addr JR addr-$ JMPR addr
Jump relative unconditional
JRC addr JR C,addr-$ JRC addr
Jump relative if Carry indicator true
JRNC addr JR NC,addr-$ JRNC addr
Jump relative if carry indicator false
JRZ addr JR Z,addr-$ JRC addr
Jump relative if Zero indicator true
JRNZ addr JR NZ,addr-$ JRNZ addr
Jump relative if Zero indicator false
DJNZ addr DJNZ addr-$ DJNZ addr
Decrement B, jump relative if non-zero
PCIX JMP (IX) PCIX
Jump to address in IX ie, Load PC from IX
PCIY JMP (IY) PCIY
Jump to address in IY
RETI RETI RETI
Return from interrupt
RETN RETN RETN
Return from non-maskable interrupt
INP r IN r,(C) INP r
Input from port C to register r
OUTP r OUT (C),r OUTP r
Output from register r to port (C)
INI INI INI
Input from port (C) to m(HL), increment HL, decrement b
INIR INIR INIR
Input from port (C) to m(HL), increment HL, decrement B, repeat if B <> 0
OUTI OTI OUTI
Output from m(HL) to port (C), increment HL, decrement B
OUTIR OTIR OUTIR
Repeat OUTI until B = 0
IND IND IND
Input from port (C) to m(HL), decrement HL & B
INDR INDR INDR
Repeat IND until B = 0
OUTD OTD OUTD
Output from m(HL) to port (C), decrement HL & B
OUTDR OTDR OUTDR
Repeat OUTD until B = 0
RLCR r RLC r RLCR r
Rotate left circular register
RLCX d RLC (IX+d) RLCR d(IX)
Rotate left circular indexed memory
RLCY d RLC (IY+d) RLCR d(IY)
Rotate left circular indexed memory
RALR r RL r RALR r
Rotate left arithmetic register
RALX d RL (IX+d) RALR d(IX)
Rotate left arithmetic indexed memory
RALY d RL (IY+d) RALR d(IY)
Rotate left arithmetic indexed memory
RRCR r RRC r RRCR r
Rotate right circular register
RRCX d RRC (IX+d) RRCR d(IX)
Rotate right circular indexed
RRCY d RRC (IY+d) RRCR d(IY)
Rotate right circular indexed
RARR r RR r RARR r
Rotate right arithmetic register
RARX d RR (IX+d) RARR d(IX)
Rotate right arithmetic indexed memory
RARY d RR (IY+d) RARR d(IY)
Rotate right arithmetic indexed memory
SLAR r SLA r SLAR r
Shift left register
SLAX d SLA (IX+d) SLAR d(IX)
Shift left indexed memory
SLAY d SLA (IY+d) SLAR d(IY)
Shift left indexed memory
SRAR r SRA r SRAR r
Shift right arithmetic register
SRAX d SRA (IX+d) SRAR d(IX)
Shift right arithmetic indexed memory
SRAY d SRA (IY+d) SRAR d(IY)
Shift right arithmetic indexed memory
SRLR r SRL r SRLR r
Shift right logical register
SRLX d SRL (IX+d) SRLR d(IX)
Shift right logical indexed memory
SRLY d SRL (IY+d) SRLR d(IY)
Shift right logical indexed memory
RLD RLD RLD
Rotate left digit
RRD RRD RRD
Rotate right digit
HK ?D
DB 0FDH, 0CBH, ?D, 16H
ENDM
RRCR MACRO ?R
DB 0CBH, 08H + ?R
ENDM
RRCX MACRO ?D
@CHK ?D
DB 0DDH, 0CBH, ?D, 0EH
ENDM
RRCY MACRO ?D
@CHK ?D
DB 0FDH, 0CBH, ?D, 0EH
ENDM
RARR MACRO ?R
DB 0CBH, 18H + ?R
ENDM
RARX MACRO ?D

View File

@@ -0,0 +1,630 @@
;;
;; Z-80 MACRO LIBRARY
;;
;; THE FOLLOWING MACROS ENABLE ASSEMBLING Z-80 INSTRUCTIONS
;; WITH THE DIGITAL RESEARCH MACRO ASSEMBLER.
;;
;; INVOKE WITH "MACLIB Z80"
;;
;;
;; Macro's added to support Z80 Opcodes by Garry Kraemer - Senior Software Engineer
;;
;;
;; MACRO FORMATS
;; ----- -------
;;
;;
;; MACRO ZILOG TDL
;; ----- ----- ---
;;
;; LDX R,D LD R,(IX+D) MOV R,D(IX)
;; LDY R,D LD R,(IY+D) MOV R,D(IY)
;; STX R,D LD (IX+D),R MOV D(IX),R
;; STY R,D LD (IY+D),R MOV D(IY),R
;; MVIX NN,D LD (IX+D),NN MVI D(IX)
;; MVIY NN,D LD (IY+D),NN MVI D(IY)
;; LDAI LD A,I LDAI
;; LDAR LD A,R LDAR
;; STAI LD I,A STAI
;; STAR LD R,A STAR
;; LXIX NNNN LD IX,NNNN LXI IX,NNNN
;; LXIY NNNN LD IY,NNNN LXI IY,NNNN
;; LBCD NNNN LD BC,(NNNN) LBCD NNNN
;; LDED NNNN LD DE,(NNNN) LDED NNNN
;; LSPD NNNN LD SP,(NNNN) LSPD NNNN
;; LIXD NNNN LD IX,(NNNN) LIXD NNNN
;; LIYD NNNN LD IY,(NNNN) LIYD NNNN
;; SBCD NNNN LD (NNNN),BC SBCD NNNN
;; SDED NNNN LD (NNNN),DE SDED NNNN
;; SSPD NNNN LD (NNNN),SP SSPD NNNN
;; SIXD NNNN LD (NNNN),IX SIXD NNNN
;; SIYD NNNN LD (NNNN),IY SIYD NNNN
;; SPIX LD SP,IX SPIX
;; SPIY LD SP,IY SPIY
;; PUSHIX PUSH IX PUSH IX
;; PUSHIY PUSH IY PUSH IY
;; POPIX POP IX POP IX
;; POPIY POP IY POP IY
;; EXAF EX AF,AF' EXAF
;; EXX EXX EXX
;; XTIX EX (SP),IX XTIX
;; XTIY EX (SP),IY XTIY
;; LDI LDI LDI
;; LDIR LDIR LDIR
;; LDD LDD LDD
;; LDDR LDDR LDDR
;; CCI CPI CCI
;; CCIR CPIR CCIR
;; CCD CPD CCD
;; CCDR CPDR CCDR
;; ADDX D ADD (IX+D) ADD D(IX)
;; ADDY D ADD (IY+D) ADD D(IY)
;; ADCX D ADC (IX+D) ADC D(IX)
;; ADCY D ADC (IY+D) ADC D(IY)
;; SUBX D SUB (IX+D) SUB D(IX)
;; SUBY D SUB (IY+D) SUB D(IY)
;; SBCX D SBC (IX+D) SBB D(IX)
;; SBCY D SBC (IY+D) SBB D(IY)
;; ANDX D AND (IX+D) ANA D(IX)
;; ANDY D AND (IY+D) ANA D(IY)
;; XORX D XOR (IX+D) XRA D(IX)
;; XORY D XOR (IY+D) XRA D(IY)
;; ORX D OR (IX+D) ORA D(IX)
;; ORY D OR (IY+D) ORA D(IY)
;; CMPX D CP (IX+D) CMP D(IX)
;; CMPY D CP (IY+D) CMP D(IY)
;; INRX D INC (IX+D) INR D(IX)
;; INRY D INC (IY+D) INR D(IY)
;; DCRX D INC (IX+D) INR D(IX)
;; DCRY D DEC (IY+D) DCR D(IY)
;; NEG NEG NEG
;; IM0 IM0 IM0
;; IM1 IM1 IM1
;; IM2 IM2 IM2
;; DADC RR ADC HL,RR DADC RR
;; DSBC RR SBC HL,RR DSBC RR
;; DADX RR ADD IX,RR DADX RR
;; DADY RR ADD IY,RR DADY RR
;; INXIX INC IX INX IX
;; INXIY INC IY INX IY
;; DCXIX DEC IX DCX IX
;; DCXIY DEC IY DCX IY
;; BIT B,R BIT B,R BIT B,R
;; SETB B,R SET B,R SET B,R
;; RES B,R RES B,R RES B,R
;; BITX B,D BIT B,(IX+D) BIT B,D(IX)
;; BITY B,D BIT B,(IY+D) BIT B,D(IY)
;; SETX B,D SET B,(IX+D) SET B,D(IX)
;; SETY B,D SET B,(IY+D) SET B,D(IY)
;; RESX B,D RES B,(IX+D) RES B,D(IX)
;; RESY B,D RES B,(IY+D) RES B,D(IY)
;; JR ADDR JR ADDR-$ JMPR ADDR
;; JRC ADDR JR C,ADDR-$ JRC ADDR
;; JRNC ADDR JR NC,ADDR-$ JRNC ADDR
;; JRZ ADDR JR Z,ADDR-$ JRC ADDR
;; JRNZ ADDR JR NZ,ADDR-$ JRNZ ADDR
;; DJNZ ADDR DJNZ ADDR-$ DJNZ ADDR
;; PCIX JMP (IX) PCIX
;; PCIY JMP (IY) PCIY
;; RETI RETI RETI
;; RETN RETN RETN
;; INP R IN R,(C) INP R
;; OUTP R OUT (C),R OUTP R
;; INI INI INI
;; INIR INIR INIR
;; OUTI OTI OUTI
;; OUTIR OTIR OUTIR
;; IND IND IND
;; INDR INDR INDR
;; OUTD OTD OUTD
;; OUTDR OTDR OUTDR
;; RLCR R RLC R RLCR R
;; RLCX D RLC (IX+D) RLCR D(IX)
;; RLCY D RLC (IY+D) RLCR D(IY)
;; RALR R RL R RALR R
;; RALX D RL (IX+D) RALR D(IX)
;; RALY D RL (IY+D) RALR D(IY)
;; RRCR R RRC R RRCR R
;; RRCX D RRC (IX+D) RRCR D(IX)
;; RRCY D RRC (IY+D) RRCR D(IY)
;; RARR R RR R RARR R
;; RARX D RR (IX+D) RARR D(IX)
;; RARY D RR (IY+D) RARR D(IY)
;; SLAR R SLA R SLAR R
;; SLAX D SLA (IX+D) SLAR D(IX)
;; SLAY D SLA (IY+D) SLAR D(IY)
;; SRAR R SRA R SRAR R
;; SRAX D SRA (IX+D) SRAR D(IX)
;; SRAY D SRA (IY+D) SRAR D(IY)
;; SRLR R SRL R SRLR R
;; SRLX D SRL (IX+D) SRLR D(IX)
;; SRLY D SRL (IY+D) SRLR D(IY)
;; RLD RLD RLD
;; RRD RRD RRD
;;
;;******************************************************************
;;
;; @CHK MACRO USED FOR CHECKING 8 BIT DISPLACMENTS
;;
@CHK MACRO ?DD ;; USED FOR CHECKING RANGE OF 8-BIT DISP.S
IF (?DD GT 07FH) AND (?DD LT 0FF80H)
'DISPLACEMENT RANGE ERROR - Z80 LIB'
ENDIF
ENDM
@CHECK MACRO ?N
?DD SET ?N-$-2
IF (?DD GT 07FH) AND (?DD LT 0FF80H)
'RANGE ERROR - Z80 LIB'
?DD SET 0FEH
ENDIF
ENDM
;;
;;
LDX MACRO ?R,?D ;;LOAD REG R FROM MEMORY AT (IX+D)
@CHK ?D
DB 0DDH,?R*8+46H,?D
ENDM
LDY MACRO ?R,?D ;;LOAD REG R FROM MEMORY AT (IY+D)
@CHK ?D
DB 0FDH,?R*8+46H,?D
ENDM
STX MACRO ?R,?D ;;STORE REG R AT MEMORY (IX+D)
@CHK ?D
DB 0DDH,70H+?R,?D
ENDM
STY MACRO ?R,?D ;;STORE REG R AT MEMORY (IY+D)
@CHK ?D
DB 0FDH,70H+?R,?D
ENDM
MVIX MACRO ?N,?D ;;MOV #N (8 BITS) INTO MEMORY AT (IX+D)
@CHK ?D
DB 0DDH,36H,?D,?N
ENDM
MVIY MACRO ?N,?D ;;MOV #N (8 BITS) INTO MEMORY AT (IY+D)
@CHK ?D
DB 0FDH,36H,?D,?N
ENDM
LDAI MACRO ;;LOAD ACC WITH REG I (INTERRUPT VECTOR)
DB 0EDH,57H
ENDM
LDAR MACRO ;;LOAD ACC WITH THE REFRESH REG
DB 0EDH,5FH
ENDM
STAI MACRO ;;STORE ACC INTO REG I
DB 0EDH,47H
ENDM
STAR MACRO ;;STORE ACC INTO THE REFRESH REG
DB 0EDH,4FH
ENDM
LXIX MACRO ?NNNN ;;LOAD IMMEDIATE (LDI) IX WITH #NNNN (16 BITS)
DB 0DDH,21H
DW ?NNNN
ENDM
LXIY MACRO ?NNNN ;;LDI REG PAIR IY WITH #NNNN (16 BITS)
DB 0FDH,21H
DW ?NNNN
ENDM
LDED MACRO ?NNNN ;;LOAD DE WITH CONTENTS OF MEMORY AT NNNN
DB 0EDH,5BH ;; (E)=((NN)(NN))
DW ?NNNN ;; (D)=((NN)(NN+1))
ENDM
LBCD MACRO ?NNNN ;;LOAD BC WITH CONTENTS OF MEMORY AT NNNN
DB 0EDH,4BH
DW ?NNNN
ENDM
LSPD MACRO ?NNNN ;;LOAD STACK POINTER WITH CONTENTS OF MEMORY
DB 0EDH,07BH ;; AT NNNN
DW ?NNNN
ENDM
LIXD MACRO ?NNNN ;;LOAD IX WITH CONTENTS OF MEMORY AT NNNN
DB 0DDH,2AH
DW ?NNNN
ENDM
LIYD MACRO ?NNNN ;;LOAD IY WITH CONTENTS OF MEMORY AT NNNN
DB 0FDH,2AH
DW ?NNNN
ENDM
SBCD MACRO ?NNNN ;;STORE BC IN MEMORY LOCATION NNNN
DB 0EDH,43H ;; ((NN)(NN))=(C)
DW ?NNNN ;; ((NN)(NN+1))=(B)
ENDM
SDED MACRO ?NNNN ;;STORE DE IN MEMORY LOC NNNN
DB 0EDH,53H
DW ?NNNN
ENDM
SSPD MACRO ?NNNN ;;STORE STACK POINTER IN MEMORY LOC NNNN
DB 0EDH,73H
DW ?NNNN
ENDM
SIXD MACRO ?NNNN ;;STORE IX IN MEMORY LOC NNNN
DB 0DDH,22H
DW ?NNNN
ENDM
SIYD MACRO ?NNNN ;;STORE IY IN MEMORY LOC NNNN
DB 0FDH,22H
DW ?NNNN
ENDM
SPIX MACRO ;;MOV IX INTO STACK POINTER (SP)
DB 0DDH,0F9H
ENDM
SPIY MACRO ;;MOV IY INTO STACK POINTER (SP)
DB 0FDH,0F9H
ENDM
PUSHIX MACRO ;;PUSH IX ONTO STACK
DB 0DDH,0E5H
ENDM
PUSHIY MACRO ;;PUSH IY ONTO STACK
DB 0FDH,0E5H
ENDM
POPIX MACRO ;;POP IX OFF STACK
DB 0DDH,0E1H
ENDM
POPIY MACRO ;;POP IY OFF STACK
DB 0FDH,0E1H
ENDM
EXAF MACRO ;;EXCHANGE AF AND AF' REGS
DB 08H
ENDM
EXX MACRO ;;EXCHANGE BC AND BC'
DB 0D9H ;; DE AND DE'
;; HL AND HL'
ENDM
XTIX MACRO ;;EXCHANGE TOP OF STACK WITH IX
DB 0DDH,0E3H
ENDM
XTIY MACRO ;;EXCHANGE TOP OF STACK WITH IY
DB 0FDH,0E3H
ENDM
LDI MACRO ;;LOAD MEMORY AT (H,L) TO MEMORY AT (D,E)
DB 0EDH,0A0H ;; INC D,E INC H,L DEC B,C
ENDM
LDIR MACRO ;;AS ABOVE REPEAT UNTIL B=0
DB 0EDH,0B0H
ENDM
LDD MACRO ;;LOAD MEMORY AT (H,L) TO MEMORY AT (D,E)
DB 0EDH,0A8H ;; DEC H,L DEC D,E DEC B,C
ENDM
LDDR MACRO ;;AS ABOVE REPEAT UNTIL B=0
DB 0EDH,0B8H
ENDM
CCI MACRO ;;COMPARE ACC WITH MEMORY AT (H,L)
DB 0EDH,0A1H ;; INC H,L DEC B,C (P/V=0 IF B=0)
;; (ACC=(H,L) IF Z=0)
ENDM
CCIR MACRO ;;AS ABOVE REPEAT UNTIL (B,C) = 0 OR
;; (A) = (H,L)
DB 0EDH,0B1H
ENDM
CCD MACRO ;;COMPARE ACC WITH MEMORY AT (H,L)
DB 0EDH,0A9H ;; DEC H,L DEC B,C
ENDM
CCDR MACRO ;;AS ABOVE REPEAT UNTIL (BC) = 0 OR
;; (A) = (H,L)
DB 0EDH,0B9H
ENDM
ADDX MACRO ?D ;;ACC=ACC+CONTENTS OF MEMORY AT (IX+D)
@CHK ?D ;; ACC=ACC+((IX+D))
DB 0DDH,86H,?D
ENDM
ADDY MACRO ?D ;;ACC=ACC+((IY+D))
@CHK ?D
DB 0FDH,86H,?D
ENDM
ADCX MACRO ?D ;;ACC=ACC+((IX+D))+CARRY
@CHK ?D
DB 0DDH,8EH,?D
ENDM
ADCY MACRO ?D ;;ACC=ACC+((IY+D))+CARRY
@CHK ?D
DB 0FDH,8EH,?D
ENDM
SUBX MACRO ?D ;;ACC=ACC-((IX+D))
@CHK ?D
DB 0DDH,96H,?D
ENDM
SUBY MACRO ?D ;;ACC=ACC-((IY+D))
@CHK ?D
DB 0FDH,96H,?D
ENDM
SBCX MACRO ?D ;;ACC=ACC-((IX+D))-BORROW
@CHK ?D
DB 0DDH,9EH,?D
ENDM
SBCY MACRO ?D ;;ACC=ACC-((IY+D))-BORROW
@CHK ?D
DB 0FDH,9EH,?D
ENDM
ANDX MACRO ?D ;;AND ACC WITH CONTENTS OF MEMORY (IX+D)
@CHK ?D
DB 0DDH,0A6H,?D
ENDM
ANDY MACRO ?D ;;AND ACC WITH CONTENTS OF MEMORY (IY+D)
@CHK ?D
DB 0FDH,0A6H,?D
ENDM
XORX MACRO ?D ;;EXCLUSIVE OR THE ACC WITH CONTEMTS OF
@CHK ?D ;; MEMORY AT (IX+D)
DB 0DDH,0AEH,?D
ENDM
XORY MACRO ?D ;;XOR ACC WITH CONTENTS OF MEMORY (IY+D)
@CHK ?D
DB 0FDH,0AEH,?D
ENDM
ORX MACRO ?D ;;OR THE ACC WITH CONTENTS OF MEMORY (IX+D)
@CHK ?D
DB 0DDH,0B6H,?D
ENDM
ORY MACRO ?D ;;OR ACC WITH CONTENTS OF MEMORY (IY+D)
@CHK ?D
DB 0FDH,0B6H,?D
ENDM
CMPX MACRO ?D ;;COMPARE THE ACC WITH CONTENTS OF MEMORY
@CHK ?D ;; AT LOC (IX+D)
DB 0DDH,0BEH,?D
ENDM
CMPY MACRO ?D ;;CMP ACC WITH CONTENTS OF MEMORY (IY+D)
@CHK ?D
DB 0FDH,0BEH,?D
ENDM
INRX MACRO ?D ;;INC MEMORY CONTENTS AT (IX+D)
@CHK ?D
DB 0DDH,34H,?D
ENDM
INRY MACRO ?D ;;INC MEMORY CONTENTS AT (IY+D)
@CHK ?D
DB 0FDH,34H,?D
ENDM
DCRX MACRO ?D ;;DEC MEMORY CONTENTS AT (IX+D)
@CHK ?D
DB 0DDH,035H,?D
ENDM
DCRY MACRO ?D ;;DEC MEMORY CONTENTS AT (IY+D)
@CHK ?D
DB 0FDH,35H,?D
ENDM
NEG MACRO ;;NEGATE ACC (2'S COMPLEMENT)
DB 0EDH,44H
ENDM
IM0 MACRO ;;SET INTERRUPT MODE #0
DB 0EDH,46H
ENDM
IM1 MACRO ;;SET INTERRUPT MODE #1 (RESTART 0038H)
DB 0EDH,56H
ENDM
IM2 MACRO ;;SET INTERRUPT MODE #2
DB 0EDH,5EH ;; RESTART AT LOC (INT REG I)(DEV SUPPLIED)
ENDM
BC EQU 0
DE EQU 2
HL EQU 4
IX EQU 4
IY EQU 4
DADC MACRO ?R ;;DOUBLE ADD (H,L)=(H,L)+(RP)+CARRY
DB 0EDH,?R*8+4AH
ENDM
DSBC MACRO ?R ;;DOUBLE SUB (H,L)=(H,L)-(RP)-CARRY
DB 0EDH,?R*8+42H
ENDM
DADX MACRO ?R ;;DOUBLE ADD (IX)=(IX)+(RP)
DB 0DDH,?R*8+09H
ENDM
DADY MACRO ?R ;;DOUBLE ADD (IY)=(IY)+(RP)
DB 0FDH,?R*8+09H
ENDM
INXIX MACRO ;;INC (IX)
DB 0DDH,23H
ENDM
INXIY MACRO ;;INC (IY)
DB 0FDH,23H
ENDM
DCXIX MACRO ;;DEC (IX)
DB 0DDH,2BH
ENDM
DCXIY MACRO ;;DEC (IY)
DB 0FDH,2BH
ENDM
BIT MACRO ?N,?R ;;TEST BIT N IN REG R
DB 0CBH,?N*8+?R+40H
ENDM
SETB MACRO ?N,?R ;;SET BIT N IN REG R
DB 0CBH,?N*8+?R+0C0H
ENDM
RES MACRO ?N,?R ;;RESET BIT N IN REG R
DB 0CBH,?N*8+?R+80H
ENDM
BITX MACRO ?N,?D ;;TEST BIT N IN MEMORY LOC (IX+D)
@CHK ?D
DB 0DDH,0CBH,?D,?N*8+46H
ENDM
BITY MACRO ?N,?D ;;TEST BIT N IN MEMORY LOC (IY+D)
@CHK ?D
DB 0FDH,0CBH,?D,?N*8+46H
ENDM
SETX MACRO ?N,?D ;;SET BIT N IN MEMORY LOC (IX+D)
@CHK ?D
DB 0DDH,0CBH,?D,?N*8+0C6H
ENDM
SETY MACRO ?N,?D ;;SET BIT N IN MEMORY LOC (IY+D)
@CHK ?D
DB 0FDH,0CBH,?D,?N*8+0C6H
ENDM
RESX MACRO ?N,?D ;;RESET BIT N IN MEMORY LOC (IX+D)
@CHK ?D
DB 0DDH,0CBH,?D,?N*8+86H
ENDM
RESY MACRO ?N,?D ;;RESET BIT N IN MEMORY LOC (IY+D)
@CHK ?D
DB 0FDH,0CBH,?D,?N*8+86H
ENDM
JR MACRO ?N ;;JUMP RELATIVE
@CHECK ?N
DB 18H,?DD
ENDM
JMPR MACRO ?N ;;JUMP RELATIVE AS ABOVE
@CHECK ?N
DB 18H,?DD
ENDM
JRC MACRO ?N ;;JUMP RELATIVE ON CARRY
@CHECK ?N
DB 38H,?DD
ENDM
JRNC MACRO ?N ;;JUMP RELATIVE ON NOT CARRY
@CHECK ?N
DB 30H,?DD
ENDM
JRZ MACRO ?N ;;JUMP RELATIVE ON ZERO
@CHECK ?N
DB 28H,?DD
ENDM
JRNZ MACRO ?N ;;JUMP RELATIVE ON NOT ZERO
@CHECK ?N
DB 20H,?DD
ENDM
DJNZ MACRO ?N ;;DEC B AND JUMP RELATIVE ON B NOT ZERO
@CHECK ?N
DB 10H,?DD
ENDM
PCIX MACRO ;;MOV IX INTO PROGRAM COUNTER
DB 0DDH,0E9H
ENDM
PCIY MACRO ;;MOV IY INTO PROGRAM COUNTER
DB 0FDH,0E9H
ENDM
RETI MACRO ;;RETURN FROM INTERRUPT (EI NOT ENABLED)
DB 0EDH,4DH
ENDM
RETN MACRO ;;RETURN FROM NON-MASKABLE INTERRUPT
DB 0EDH,45H
ENDM
RETEI MACRO ;;RETURN FROM INTERRUPT (EI ENABLED)
DB EI,0EDH,4DH
ENDM
INP MACRO ?R ;;GO TO INPUT PORT (SPECIFIED BY REG C)
DB 0EDH,?R*8+40H ;; AND LOAD INTO REG R
ENDM
OUTP MACRO ?R ;;OUTPUT TO PORT (SPECIFIED BY REG C)
DB 0EDH,?R*8+41H ;; FROM REG R
ENDM
INI MACRO ;;LOAD MEMORY (H,L) WITH INPUT PORT
DB 0EDH,0A2H ;; (SPECIFIED BY REG C) INC H,L DEC B
ENDM
INIR MACRO ;;AS ABOVE REPEAT UNTIL B=0
DB 0EDH,0B2H
ENDM
IND MACRO ;;LOAD MEMORY (H,L) WITH INPUT PORT
DB 0EDH,0AAH ;; (SPECIFIED BY REG C) DEC H,L DEC B
ENDM
INDR MACRO ;;AS ABOVE REPEAT UNTIL B=0
DB 0EDH,0BAH
ENDM
OUTI MACRO ;;OUTPUT TO PORT (SPECIFIED BY REG C)
DB 0EDH,0A3H ;; FROM MEMORY (H,L) INC H,L DEC B
ENDM
OUTIR MACRO ;;AS ABOVE REPEAT UNTIL B=0
DB 0EDH,0B3H
ENDM
OUTD MACRO ;;OUTPUT TO PORT (SPECIFIED BY REG C)
DB 0EDH,0ABH ;; FROM MEMORY (H,L) DEC H,L DEC B
ENDM
OUTDR MACRO ;;AS ABOVE REPEAT UNTIL B=0
DB 0EDH,0BBH
ENDM
RLCR MACRO ?R ;;ROTATE LEFT CIRCULAR REG R
DB 0CBH, 00H + ?R
ENDM
RLCX MACRO ?D ;;ROTATE LEFT CIRCULAR CONTENTS OF MEMORY
;; AT (IX+D)
@CHK ?D
DB 0DDH, 0CBH, ?D, 06H
ENDM
RLCY MACRO ?D ;;ROTATE LEFT CIRCULAR CONTENTS OF MEMORY
;; AT (IY+D)
@CHK ?D
DB 0FDH, 0CBH, ?D, 06H
ENDM
RALR MACRO ?R ;;ROTATE ARITHMETIC LEFT REG R
DB 0CBH, 10H+?R
ENDM
RALX MACRO ?D ;;ROTATE ARITHMETIC LEFT CONTENTS OF MEMORY
;; AT (IX+D)
@CHK ?D
DB 0DDH, 0CBH, ?D, 16H
ENDM
RALY MACRO ?D ;;ROTATE ARITHMETIC LEFT CONTENTS OF MEMORY
;; AT (IY+D)
@CHK ?D
DB 0FDH, 0CBH, ?D, 16H
ENDM
RRCR MACRO ?R ;;ROTATE RIGHT CIRCULAR REG R
DB 0CBH, 08H + ?R
ENDM
RRCX MACRO ?D ;;ROTATE RIGHT CIRCULAR CONTENTS OF MEMORY
;; AT (IX+D)
@CHK ?D
DB 0DDH, 0CBH, ?D, 0EH
ENDM
RRCY MACRO ?D ;;ROTATE RIGHT CIRCULAR CONTENTS OF MEMORY
;; AT (IY+D)
@CHK ?D
DB 0FDH, 0CBH, ?D, 0EH
ENDM
RARR MACRO ?R ;;ROTATE ARITHMETIC RIGHT REG R
DB 0CBH, 18H + ?R
ENDM
RARX MACRO ?D ;;ROTATE ARITHMETIC RIGHT CONTENTS OF MEMORY
;; AT (IX+D)
@CHK ?D
DB 0DDH, 0CBH, ?D, 1EH
ENDM
RARY MACRO ?D ;;ROTATE ARITHMETIC RIGHT CONTENTS OF MEMORY
;; AT (IY+D)
@CHK ?D
DB 0FDH, 0CBH, ?D, 1EH
ENDM
SLAR MACRO ?R ;;SHIFT LEFT ARITHMETIC REG R
DB 0CBH, 20H + ?R
ENDM
SLAX MACRO ?D ;;SHIFT LEFT ARITHMETIC CONTENTS OF MEMORY
;; AT(IX+D)
@CHK ?D
DB 0DDH, 0CBH, ?D, 26H
ENDM
SLAY MACRO ?D ;;SHIFT LEFT ARITHMETIC CONTENTS OF MEMORY
;; AT (IY+D)
@CHK ?D
DB 0FDH, 0CBH, ?D, 26H
ENDM
SRAR MACRO ?R ;;SHIFT RIGHT ARITHMETIC REG R
DB 0CBH, 28H+?R
ENDM
SRAX MACRO ?D ;;SHIFT RIGHT ARITHMETIC CONTENTS OF MEMORY
;; AT (IX+D)
@CHK ?D
DB 0DDH, 0CBH, ?D, 2EH
ENDM
SRAY MACRO ?D ;;SHIFT RIGHT ARITHMETIC CONTENTS OF MEMORY
;; AT (IY+D)
@CHK ?D
DB 0FDH, 0CBH, ?D, 2EH
ENDM
SRLR MACRO ?R ;;SHIFT RIGHT LOGICALLY REG R
DB 0CBH, 38H + ?R
ENDM
SRLX MACRO ?D ;;SHIFT RIGHT LOGICALLY CONTENTS OF MEMORY
;; AT (IX+D)
@CHK ?D
DB 0DDH, 0CBH, ?D, 3EH
ENDM
SRLY MACRO ?D ;;SHIFT RIGHT LOGICALLY CONTENTS OF MEMORY
;; AT (IY+D)
@CHK ?D
DB 0FDH, 0CBH, ?D, 3EH
ENDM
RLD MACRO ;;ROTATE NIBBLE LEFT & RIGHT BETWEEN ACC
DB 0EDH, 6FH ;; AND CONTENTS AT (H,L)
ENDM
RRD MACRO ;;ROTATE NIBBLE RIGHT & LEFT BETWEEN ACC
DB 0EDH, 67H ;; AND CONTENTS AT (H,L)
ENDM

View File

@@ -0,0 +1,88 @@
This is the Text Document for MOSS22, the Altair 8800 Monitor Program - MOSS Ver 2.2
The files contained in folder 8800-Manual are:
CCS_2810_Z80_CPU_1980.pdf - Original manual
MOSS22.PDF - Monitor Pages - extracted
MOSS22.TXT - Text
The Monitor (MOSS22) is described in detail in Chapter 3 of the Original Manual.
The appropriate PDF Pages that contain the Monitor Source Code are included in MOSS22.PDF.
The files contained in folder tif are the *.tif images of the PDF pages to be OCR'd.
page1.tif
page2.tif
page3.tif
page4.tif
page5.tif
page6.tif
page7.tif
page8.tif
page9.tif
page10.tif
page11.tif
page12.tif
page13.tif
page14.tif
page15.tif
page16.tif
page17.tif
page18.tif
page19.tif
page20.tif
page21.tif
page22.tif
page23.tif
page24.tif
Those pages were extracted, converted to tif files (450 DPI) and OCR'd with TextBridge Classic.
The Text file was then edited, aligned, and proof read several times to give the MOSS22.ASM file.
The files contained in folder MOSS22 are:
MOSS22.ASM
MOSS22.PRN
MOSS22.HEX
MOSS22.SYM
The files contained in folder Z80LIB are:
Z80.LIB
Z80.DOC
The Z80.LIB has to be the file with 9856 Bytes, and needs to be modified so the source assembles properly.
The above file, has already been modified.
References:
http://z80cpu.eu/files/archive/roche/Z80LIB.TXT
http://www.retroarchive.org/cpm/os/
Z80.LIB = 9856 Bytes
The DRI Assemblers and Manuals can be downloaded from:
http://www.s100computers.com/Software%20Folder/Assembler%20Collection/Assembler%20Collection.htm
Copy the following files to B:
MAC.COM
MOSS22.ASM
Z80.LIB
Z80.DOC
Execute the DRI Assembler MAC.COM in CP/M 2.2 with:
B:
MAC MOSS22.ASM
It will Build:
MOSS22.HEX
MOSS22.PRN
MOSS22.SYM
Compare the MOSS22.PRN file with the MOS22.PDF file for identical bytes.
Larry Kraemer & Garry Kraemer

View File

@@ -0,0 +1,90 @@
Altair 8800 Monitor (MOS Ver 2.2) Source Code, thanks to Larry Kraemer.
/readme.1st
This is the Text Document for MOSS22, the Altair 8800 Monitor Program - MOSS Ver 2.2
The files contained in folder 8800-Manual are:
CCS_2810_Z80_CPU_1980.pdf - Original manual
MOSS22.PDF - Monitor Pages - extracted
MOSS22.TXT - Text
The Monitor (MOSS22) is described in detail in Chapter 3 of the Original Manual.
The appropriate PDF Pages that contain the Monitor Source Code are included in MOSS22.PDF.
The files contained in folder tif are the *.tif images of the PDF pages to be OCR'd.
page1.tif
page2.tif
page3.tif
page4.tif
page5.tif
page6.tif
page7.tif
page8.tif
page9.tif
page10.tif
page11.tif
page12.tif
page13.tif
page14.tif
page15.tif
page16.tif
page17.tif
page18.tif
page19.tif
page20.tif
page21.tif
page22.tif
page23.tif
page24.tif
Those pages were extracted, converted to tif files (450 DPI) and OCR'd with TextBridge Classic.
The Text file was then edited, aligned, and proof read several times to give the MOSS22.ASM file.
The files contained in folder MOSS22 are:
MOSS22.ASM
MOSS22.PRN
MOSS22.HEX
MOSS22.SYM
The files contained in folder Z80LIB are:
Z80.LIB
Z80.DOC
The Z80.LIB has to be the file with 9856 Bytes, and needs to be modified so the source assembles properly.
The above file, has already been modified.
References:
http://z80cpu.eu/files/archive/roche/Z80LIB.TXT
http://www.retroarchive.org/cpm/os/
Z80.LIB = 9856 Bytes
The DRI Assemblers and Manuals can be downloaded from:
http://www.s100computers.com/Software%20Folder/Assembler%20Collection/Assembler%20Collection.htm
Copy the following files to B:
MAC.COM
MOSS22.ASM
Z80.LIB
Z80.DOC
Execute the DRI Assembler MAC.COM in CP/M 2.2 with:
B:
MAC MOSS22.ASM
It will Build:
MOSS22.HEX
MOSS22.PRN
MOSS22.SYM
Compare the MOSS22.PRN file with the MOS22.PDF file for identical bytes.
Larry Kraemer & Garry Kraemer

View File

@@ -0,0 +1,37 @@
#!/bin/bash
#
#This script will extrace all pages of a PDF to Sequential pages, then convert those PDF
#pages to text using TextBridge
#
#
#Larry Kraemer 09-21-2014 Chaffee, MO
#
#Splitting files
#
#Splitting PDF files with pdftk was an interesting experience. The burst option breaks a PDF into #multiple files -- one file for each page:
#
# To test the conversion try these commands: (I have better luck with TextBridge Conversions)
# convert -monochrome -density 300 pg_0001.pdf pg001.tif
# tesseract pg001.tif pg001 -psm 6
# OR Use Textbridge to convert tif files to TXT as spreadsheet or table
#
#
PAGES=24 # set to the number of PDF pages to process
SOURCE=MOSS22.pdf # set to the file name of the multipage PDF
#OUTPUT=moss22.txt # creates the final "output file"
RESOLUTION=400 # set to the resolution the scanner used (the higher, the better)
#
touch $OUTPUT
for i in `seq 1 $PAGES`; do
convert -monochrome -density $RESOLUTION $SOURCE\[$(($i - 1 ))\] page$i.tif
# tesseract page$i.tif page$i -psm 6
# cat $OUTPUT page$i.txt > temp.txt
# rm $OUTPUT
# rm page$i.tif
# rm page$i.txt
# mv temp.txt $OUTPUT
#
# OR Use Textbridge to convert tif files to TXT as spreadsheet or table
#
done

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,88 @@
[READ.ME]
The Programmer's CP/M Handbook Source Code Examples
Version 1.0 August 18, 1983
(c) 1983 Johnson-Laird Inc.
We have tried to include as many of the examples from the CP/M
Programmer's Handbook as was possible to fit onto 2 single-sided
single-density diskettes (or alternately, 1 single-density
double-sided "flippy" diskette). Our original intention was to
include the source code for every figure in the book as well as all
the example listings in Chapter 5. Since this proved to be impossible
to fit onto two diskettes, we have chosen those figures and examples
which we felt would be the most useful. The only major omission is
Figure 9-4 which deals with bad sector management.
We have fixed one bug and added one line to Figure 8-10. This is
noted in the source code. The lines which were left out in the book
after page 261 are also included. You may notice some minor
differences in capitalization, indentation, etc. in the comments where
we have not included changes made in copyediting.
We would appreciate hearing from you, especially about any bugs,
typos, other horrible goofs, suggestions for improvements.
Contents of Diskette No 1
Filename Page No. Figure Title
FIG5-2.ASM 70 Equates for BDOS function code numbers
FIG5-3.ASM 74 Write console byte example, output null-byte
terminated message from specified address
FIG5-4.ASM 74 Write console byte example, output null-byte
terminated message following call to subroutine
FIG5-5.ASM 76 Read line from reader device
FIG5-6.ASM 78 Write line to punch device
FIG5-7.ASM 79 Write line to list device
FIG5-8.ASM 81 Read/Write string from/to console using raw I/O
FIG5-10.ASM 86 IOBYTE equates
FIG5-11.ASM 87 Simple terminal emulator
FIG5-12.ASM 89 Display $-terminated message on console
FIG5-13.ASM 92 Read console string for keyboard options
FIG5-14.ASM 95 Determine the CP/M version number
FIG5-15.ASM 96 Reset requested disk drive
FIG5-16.ASM 100 Open file request
FIG5-17.ASM 104 Search first/next calls for ambiguous filename
FIG5-18.ASM 110 Read next character from sequential disk file
FIG5-19.ASM 113 Write next character to sequential disk file
FIG5-20.ASM 115 Create file request
FIG5-21.ASM 117 Rename file request
FIG5-22.ASM 122 Set file attributes
FIG5-23.ASM 123 Get file attributes
FIG5-24.ASM 126 Accessing disk parameter block data
FUNCTN33.ASM 131 Example for function 33, read random
FIG5-25.ASM 135 Create random file
FIG5-26.ASM 136 Read/write variable length records randomly
FIG6-4.ASM 159 Simple BIOS listing
FIG7-5.ASM 191 Example PUTCP/M
FIG7-7.ASM 198 Example CP/M cold bootstrap loader
FIG8-6.ASM 226 Device table equates
FIG10-5.ASM 363 Testbed for real time clock driver in the BIOS
FIG10-6.ASM 365 Testbed for disk I/O drivers in the BIOS
ERASE.C 410 Figure 11-3, requests confirmation before erasing
UNERASE.C 412 Figure 11-4, "revives" erased files
FIND.C 417 Figure 11-5, locates specific files or groups of files
SPACE.C 420 Figure 11-6, displays how much disk storage is used
or available
MOVE.C 424 Figure 11-7, "moves" files from one user to another
MAKE.C 428 Figure 11-8, makes files "invisible" and protected
or makes them "visible," accessible and unprotected
SPEED.C 431 Figure 11-9, sets the baud rate for a specific device
PROTOCOL.C 435 Figure 11-10, sets the protocol governing input and output
of a specified serial device
ASSIGN.C 439 Figure 11-11, assigns a logical devices input and output
to two or more physical devices
DATE.C 443 Figure 11-12, makes the current date part of the system
TIME.C 444 Figure 11-13, makes the current time part of the system
FUNKEY.C 446 Figure 11-14, sets the character strings associated with
specific function keys
Contents of Diskette 2
FIG8-10.ASM 237 Enhanced BIOS listing
FIG9-5.ASM 312 User-friendly disk error processor
FIG10-2.ASM 331 Debug subroutines
FIG10-4.ASM 355 Testbed for character I/O drivers
LIBRARY.C 372 Figure 11-1, commonly used functions in C
LIBRARY.H 390 Figure 11-2, code to be included at the beginning of
any program that calls LIBRARY functions in Figure 11-1

View File

@@ -0,0 +1,121 @@
Source code examples from "The Programmers CP/M Handbook": by Andy Johnson-Laird, copyright 1983 by Osborne/McGraw-Hill. Andy has given his permission to share this code for non-commercial use.
/READ-ME.txt
[READ.ME]
The Programmer's CP/M Handbook Source Code Examples
Version 1.0 August 18, 1983
(c) 1983 Johnson-Laird Inc.
We have tried to include as many of the examples from the CP/M
Programmer's Handbook as was possible to fit onto 2 single-sided
single-density diskettes (or alternately, 1 single-density
double-sided "flippy" diskette). Our original intention was to
include the source code for every figure in the book as well as all
the example listings in Chapter 5. Since this proved to be impossible
to fit onto two diskettes, we have chosen those figures and examples
which we felt would be the most useful. The only major omission is
Figure 9-4 which deals with bad sector management.
We have fixed one bug and added one line to Figure 8-10. This is
noted in the source code. The lines which were left out in the book
after page 261 are also included. You may notice some minor
differences in capitalization, indentation, etc. in the comments where
we have not included changes made in copyediting.
We would appreciate hearing from you, especially about any bugs,
typos, other horrible goofs, suggestions for improvements.
Contents of Diskette No 1
Filename Page No. Figure Title
FIG5-2.ASM 70 Equates for BDOS function code numbers
FIG5-3.ASM 74 Write console byte example, output null-byte
terminated message from specified address
FIG5-4.ASM 74 Write console byte example, output null-byte
terminated message following call to subroutine
FIG5-5.ASM 76 Read line from reader device
FIG5-6.ASM 78 Write line to punch device
FIG5-7.ASM 79 Write line to list device
FIG5-8.ASM 81 Read/Write string from/to console using raw I/O
FIG5-10.ASM 86 IOBYTE equates
FIG5-11.ASM 87 Simple terminal emulator
FIG5-12.ASM 89 Display $-terminated message on console
FIG5-13.ASM 92 Read console string for keyboard options
FIG5-14.ASM 95 Determine the CP/M version number
FIG5-15.ASM 96 Reset requested disk drive
FIG5-16.ASM 100 Open file request
FIG5-17.ASM 104 Search first/next calls for ambiguous filename
FIG5-18.ASM 110 Read next character from sequential disk file
FIG5-19.ASM 113 Write next character to sequential disk file
FIG5-20.ASM 115 Create file request
FIG5-21.ASM 117 Rename file request
FIG5-22.ASM 122 Set file attributes
FIG5-23.ASM 123 Get file attributes
FIG5-24.ASM 126 Accessing disk parameter block data
FUNCTN33.ASM 131 Example for function 33, read random
FIG5-25.ASM 135 Create random file
FIG5-26.ASM 136 Read/write variable length records randomly
FIG6-4.ASM 159 Simple BIOS listing
FIG7-5.ASM 191 Example PUTCP/M
FIG7-7.ASM 198 Example CP/M cold bootstrap loader
FIG8-6.ASM 226 Device table equates
FIG10-5.ASM 363 Testbed for real time clock driver in the BIOS
FIG10-6.ASM 365 Testbed for disk I/O drivers in the BIOS
ERASE.C 410 Figure 11-3, requests confirmation before erasing
UNERASE.C 412 Figure 11-4, "revives" erased files
FIND.C 417 Figure 11-5, locates specific files or groups of files
SPACE.C 420 Figure 11-6, displays how much disk storage is used
or available
MOVE.C 424 Figure 11-7, "moves" files from one user to another
MAKE.C 428 Figure 11-8, makes files "invisible" and protected
or makes them "visible," accessible and unprotected
SPEED.C 431 Figure 11-9, sets the baud rate for a specific device
PROTOCOL.C 435 Figure 11-10, sets the protocol governing input and output
of a specified serial device
ASSIGN.C 439 Figure 11-11, assigns a logical devices input and output
to two or more physical devices
DATE.C 443 Figure 11-12, makes the current date part of the system
TIME.C 444 Figure 11-13, makes the current time part of the system
FUNKEY.C 446 Figure 11-14, sets the character strings associated with
specific function keys
Contents of Diskette 2
FIG8-10.ASM 237 Enhanced BIOS listing
FIG9-5.ASM 312 User-friendly disk error processor
FIG10-2.ASM 331 Debug subroutines
FIG10-4.ASM 355 Testbed for character I/O drivers
LIBRARY.C 372 Figure 11-1, commonly used functions in C
LIBRARY.H 390 Figure 11-2, code to be included at the beginning of
any program that calls LIBRARY functions in Figure 11-1
/_dedication.txt
The included files are the source code examples from Andy Johnson-Laird's book, "The Programmers CP/M Handbook", copyright 1983 by Osborne/McGraw-Hill.
Andy has given his permission to share this code for non-commercial use and asked that the following information be included with this distribution -
*******************************************
Please give me credit for the code.
You might even consider putting in a two line tribute to the late Gary Kildall.
Andy has requested that those who use this code pause to consider where we would
be if the late Gary Kildall, a pioneer's pioneer, had not taken the time to write CP/M.
If you want to know the *real* story, I can recommend the book "They Made America" by Sir Harold Evans. It will tell you just how significant Gary's work was, and how Mr. Gates got to where he is.
The second edition has a far more complete story.
Regards
Andy
********************************************
Posted 10/8/07
Jack Rubin

View File

@@ -0,0 +1,23 @@
The included files are the source code examples from Andy Johnson-Laird's book, "The Programmers CP/M Handbook", copyright 1983 by Osborne/McGraw-Hill.
Andy has given his permission to share this code for non-commercial use and asked that the following information be included with this distribution -
*******************************************
Please give me credit for the code.
You might even consider putting in a two line tribute to the late Gary Kildall.
Andy has requested that those who use this code pause to consider where we would
be if the late Gary Kildall, a pioneer's pioneer, had not taken the time to write CP/M.
If you want to know the *real* story, I can recommend the book "They Made America" by Sir Harold Evans. It will tell you just how significant Gary's work was, and how Mr. Gates got to where he is.
The second edition has a far more complete story.
Regards
Andy
********************************************
Posted 10/8/07
Jack Rubin

View File

@@ -0,0 +1,230 @@
#define VN "\nASSIGN Vn 1.0 02/17/83"
#include <LIBRARY.H>
struct _ct ct_pdev[MAXPDEV + 2]; /* physical device table */
/* names of logical devices */
#define LN_C "CONSOLE"
#define LN_A "AUXILIARY"
#define LN_L "LIST"
struct _ct ct_ldev[4]; /* logical device table */
struct _ct ct_io[3]; /* input, output */
/* parameters on the command line */
#define LDEV argv[1] /* logical device */
#define IO argv[2] /* input/output */
main(argc,argv)
int argc;
char *argv[];
{
printf(VN); /* display signon message */
setup(); /* setup code tables */
chk_use(argc); /* check correct usage */
/* check if request to show current settings */
if (usstrcmp("SHOW",argv[1]))
{ /* no, assume a set is required */
/* NOTE : the number of physical devices to
process is given by argc -3 */
set_assign(get_ldev(LDEV),get_io(IO),argc - 3,argv);
}
show_assign();
}
setup() /* setup the code tables for this program */
{
/* initialize the physical device table */
ct_init(ct_pdev[0],0,PN_T); /* terminal */
ct_init(ct_pdev[1],1,PN_P); /* printer */
ct_init(ct_pdev[2],2,PN_M); /* modem */
ct_init(ct_pdev[3],CT_SNF,"*"); /* terminator */
/* initialize the logical device table */
ct_init(ct_ldev[0],0,LN_C); /* terminal */
ct_init(ct_ldev[1],1,LN_A); /* auxiliary */
ct_init(ct_ldev[2],2,LN_L); /* list */
ct_init(ct_ldev[3],CT_SNF,"*"); /* terminator */
/* initialize the input/output table */
ct_init(ct_io[0],0,"INPUT");
ct_init(ct_io[1],1,"OUTPUT");
ct_init(ct_io[2],CT_SNF,"*"); /* terminator */
}
unsigned
get_ldev(pldev) /* get logical device */
/* This function returns the logical device code
specified by the user in the command line. */
char *pldev; /* pointer to character string */
{
unsigned retval; /* return value */
retval = ct_parc(ct_ldev,pldev); /* get code for ASCII string */
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Logical Device '%s' is invalid or ambiguous.",
pldev);
printf("\nLegal Logical Devices are : ");
ct_disps(ct_ldev); /* display all values */
exit();
}
return retval; /* return code */
}
unsigned
get_io(pio) /* get input/output parameter */
char *pio; /* pointer to character string */
{
unsigned retval; /* return value */
retval = ct_parc(ct_io,pio); /* get code for ASCII string */
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Input/Output direction '%s' is invalid or ambiguous.",
pio);
printf("\nLegal values are : ");
ct_disps(ct_io); /* display all values */
exit();
}
return retval; /* return code */
}
set_assign(ldevc,output,argc,argv) /* set assignment (i/o redirection) */
int ldevc; /* logical device code */
int output; /* i/o redirection code */
int argc; /* count of arguments to process */
char *argv[]; /* replica of parameter to main function */
{
unsigned *redir; /* pointer to redirection word */
int pdevc; /* physical device code */
unsigned rd_val; /* redirection value */
/* get the address of the i/o redirection word.
this code assumes that get_cba code values
are ordered :
Device #0, Input & Output
Device #1, Input & Output
Device #2, Input & Output.
the get_cba code is computed by multiplying the
logical device code * 2 (that is, shift left 1)
and added onto the code for Device #0, Input.
then the output variable (0 = Input, 1 = Output)
is added on. */
redir = get_cba(CB_CI + (ldevc << 1) + output);
rd_val = 0; /* initialize redirection value */
/* for output, assignment can be made to several physical
devices, so this code may be executed several times. */
do
{
/* get code for ASCII string */
/* NOTE : the physical device parameters start
with parameter #3 (argv[3]). However argc
is a decreasing count of the number of physical
devices to be processed - therefore argc + 2
causes them to be processed in reverse order
(i.e. from right to left on the command line) */
pdevc = ct_parc(ct_pdev,argv[argc + 2]);
if (pdevc == CT_SNF) /* if string not found */
{
printf("\n\007Physical Device '%s' is invalid or ambiguous.",
argv[argc + 2]);
printf("\nLegal Physical Devices are : ");
ct_disps(ct_pdev); /* display all values */
exit();
}
/* repeat this loop for as long as there are
more parameters (for output only) */
else
{
/* build new redirection value by OR'ing in
a 1-bit shifted left pdevc places. */
rd_val |= (1 << pdevc);
}
} while (--argc && output);
*redir = rd_val; /* set the value into the config. block */
}
show_assign() /* show current baud rate */
{
int rd_code; /* redirection code for get_cba */
int ldevn; /* logical device number */
int pdevn; /* physical device number */
unsigned rd_val; /* re-direction value */
unsigned *prd_val; /* pointer to the redirection value */
/* Note : the respective codes for accessing the redirection values
via the get_cba (get configuration block address) function are:
Device #0 Console Input - 5
Device #0 Console Output - 6
Device #1 Auxiliary Input - 7
Device #1 Auxiliary Output - 8
Device #2 List Input - 9
Device #2 List Output - 10
This function uses this mathematical relationship. */
printf("\nCurrent Device Assignments are :");
/* for all get_cba codes */
for (rd_code = CB_CI; rd_code <= CB_LO; rd_code++)
{
/* set pointer to redirection value */
prd_val = get_cba(rd_code);
/* get the input redirection value */
rd_val = *prd_val; /* this also performs byte-reversal */
/* display device name. rd_code is converted to a
device number by subtracting the first code number
from it and dividing by 2 (shift right one place).
the input/output direction is derived from the
least significant bit of the rd_code. */
printf("\n\t%s %s is assigned to - ",
ct_strc(ct_ldev,(rd_code - CB_CI) >> 1),
ct_strc(ct_io,((rd_code & 0x01) ^ 1)));
/* for all physical devices */
for (pdevn = 0; pdevn < 16; pdevn++)
{
/* check if current physical device is assigned
by ANDing with a 1-bit shifted left pdevn times */
if (rd_val & (1 << pdevn)) /* is device active */
{ /* display physical device name */
printf(" %s",ct_strc(ct_pdev,pdevn) );
}
}
}
}
chk_use(argc) /* check for correct usage */
int argc; /* argument count on commmand line */
{
if (argc == 1)
{
printf("\nASSIGN sets the Input/Output redirection.");
printf("\n\tASSIGN logical-device INPUT physical-device");
printf("\n\tASSIGN logical-device OUTPUT physical-dev1 {phy_dev2..}");
printf("\n\tASSIGN SHOW (to show current assignments)");
printf("\n\nLegal logical devices are :");
ct_disps(ct_ldev);
printf("\nLegal physical devices are :");
ct_disps(ct_pdev);
exit();
}
}


View File

@@ -0,0 +1,79 @@
#define VN "\nDATE Vn 1.0 02/18/83"
/* This utility accepts the current date from the command tail
validates it, and set the internal system date in the BIOS.
Alternatively, it can be requested just to display the current
system date. */
#include <LIBRARY.H>
char *date; /* pointer to the date in the config. block */
char *date_flag; /* pointer to date set flag */
int mm,dd,yy; /* variables to hold month, day, year */
int mcount; /* match count of numeric values entered */
int count; /* count used to add leading 0's to date */
main(argc,argv)
int argc;
char *argv[];
{
printf(VN); /* display signon message */
date = get_cba(CB_DATE); /* set pointer to date */
date_flag = get_cba(CB_DTFLAGS); /* set pointer to "date set" flag */
if (argc != 2) /* check if help requested (or needed) */
show_use(); /* display correct usage and exit */
if (usstrcmp("SHOW",argv[1])) /* check if not SHOW option */
{
/* convert specified time into month, day, year */
mcount = sscanf(argv[1],"%d/%d/%d",&mm,&dd,&yy);
if (mcount != 3) /* input not numeric */
show_use(); /* display correct usage and exit */
/* NOTE : The following validity checking is VERY
simplistic - this could be expanded to accomodate
more context-sensitive checking, days in the month,
leap years, etc. */
if (mm > 12 || mm < 1) /* check valid month, day, year */
{
printf("\nMonth = %d is illegal.",mm);
show_use(); /* display correct usage and exit */
}
if (dd > 31 || dd < 1)
{
printf("\nDay = %d is illegal.",dd);
show_use(); /* display correct usage and exit */
}
if (yy > 90 || yy < 83) /* <=== NOTE ! */
{
printf("\nYear = %d is illegal.",yy);
show_use(); /* display correct usage and exit */
}
/* convert integers back into a formatted string */
sprintf(date,"%2d/%2d/%2d",mm,dd,yy);
date[8] = 0x0A; /* terminate with line feed */
date[9] = '\0'; /* new string terminator */
/* change " 1/ 2/ 3" into "01/02/03" */
for (count = 0; count < 7; count+=3)
{
if (date[count] == ' ')
date[count] = '0';
}
/* turn flag on to indicate that the user has set the date */
*date_flag |= DATE_SET;
}
printf("\n\tCurrent Date is %s",date);
}
show_use() /* display correct usage and exit */
{
printf("\nDATE sets the system date. Usage is :");
printf("\n\tDATE mm/dd/yy");
printf("\n\tDATE SHOW (to display current date)\n");
exit();
}


View File

@@ -0,0 +1,107 @@
#define VN "1.0 02/24/83"
/* ERASE
This utility erases the specified file(s). It performs a logical
erasure by using a BDOS Delete function. */
#include <LIBRARY.H>
struct _fcb amb_fcb; /* ambiguous name file control block */
struct _fcb fcb; /* used for BDOS Search Functions */
char file_name[20]; /* formatted for display : d:FILENAME.TYP */
short cur_disk; /* current logical disk at start of program */
/* ERASE saves up the FCB's of the all the
files that need to be erased in the
following array. */
#define MAXERA 1024
struct _fcb era_fcb[MAXERA];
int ecount; /* count of number of files to be erased */
int count; /* used to access era_fcb during erasing */
main(argc,argv)
short argc; /* argument count */
char *argv[]; /* argument vector (pointer to an array of chars) */
{
printf("\nERASE Version %s (Library %s)",VN,LIBVN);
chk_use(argc); /* check usage */
cur_disk = bdos(GETDISK); /* get current default disk */
ecount = 0; /* initialize count of files to erase */
setfcb(amb_fcb,argv[1]); /* set ambiguous file name */
if (amb_fcb.fcb_disk) /* check if default disk to be used */
{
bdos(SETDISK,amb_fcb.fcb_disk + 1); /* set to specified disk */
}
/* convert ambiguous file name for output */
conv_fname(amb_fcb,file_name);
printf("\n\nSearching for file(s) matching %s.",file_name);
/* set the file control block to indicate a "first" search */
fcb.fcb_disk |= 0x80; /* OR in the ms bit */
/* while not at the end of the directory, set the FCB
to the next name that matches */
while(get_nfn(amb_fcb,fcb))
{
conv_fname(fcb,file_name);
/* ask whether to erase file or not */
printf("\n\tErase %s y/n? ",file_name);
if (toupper(getchar()) == 'Y')
{
printf(" <== Will be Erased!");
/* add current fcb to array of fcb's */
movmem(fcb,&era_fcb[ecount++],FCBSIZE);
/* check that the table is not full */
if (ecount == MAXERA)
{
printf("\nWarning : Internal Table now full. No more files can be erased");
printf("\n until those already specified have been erased.");
break; /* break out of while loop */
}
}
} /* all directory entries processed */
if (ecount)
printf("\n\nErasing files now...");
/* now process each fcb in the array, erasing the files */
for (count = 0; /* starting with the first file in the array */
count < ecount; /* until all active entries processed */
count++) /* move to next fcb */
{
conv_fname(&era_fcb[count],file_name);
if (bdos(DELETEF,&era_fcb[count]) == -1) /* error? */
printf("\n\007Error trying to erase %s",file_name);
else /* file erased */
printf("\n\tFile %s erased.",file_name);
}
bdos(SETDISK,cur_disk); /* reset to current disk */
}
chk_use(argc) /* check usage */
/* This function checks that the correct number of
parameters has been specified, outputting instructions
if not. */
/* Entry Parameter */
int argc; /* Count of the number of arguments on the command line */
{
/* The minimum value of argc is 1 (for the program name itself),
so argc is always one greater than the number of parameters
on the command line */
if (argc != 2)
{
printf("\nUsage :");
printf("\n\tERASE {d:}file_name.typ");
exit();
}
}


File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
; Figure 10-4
;
; Testbed for Character I/O drivers in the BIOS.
;
; The complete source file consists of three components:
;
; 1. The testbed code shown here.
; 2. The Character I/O drivers destined for the BIOS.
; 3. The debug package shown in Figure 10-2.
;
TRUE EQU 0FFFFH
FALSE EQU NOT TRUE
DEBUG EQU TRUE ;For conditional assembly of RST
; instructions in place of IN and
; OUT instructions in the drivers.
RST6 EQU 30H ;Use RST 6 for fake incoming character
; interrupt.
ORG 100H
START:
LXI SP,Test$Stack ;Use a local stack
CALL DB$Init ;Initialize the debug package
MVI A,JMP ;Setup RST 6 with JMP opcode
STA RST6
LXI H,Character$Interrupt ;Setup RST 6 JMP address
SHLD RST6 + 1
;
; Make repeated entry to Character Interrupt routine
; to ensure that characters can be captured and stored in
; an input buffer
;
Testbed$Loop:
MVI A,0AAH ;Set registers to known pattern
LXI B,0BBCCH
LXI D,0DDEEH
LXI H,0FF11H
RST 6 ;Fake interrupt for incoming character
CALL DB$MSGI ;Display in-line message
DB 0DH,0AH,'Enter I to Input Char., O to Output, D to enter '
DB 'DDT : ',0
CALL DB$CONINU ;Get upper case character
CPI 'I' ;CONIN?
JZ Go$CONIN
CPI 'D' ;DDT?
JZ Go$DDT
CPI 'O' ;CONOUT?
JZ Go$CONOUT
JMP Testbed$Loop ;Loop back to interrupt again
Go$DDT:
RST 7 ;Enter DDT (RST 7 setup by DDT)
JMP Testbed$Loop
Go$CONIN:
CALL CONST ;Get Console Status
JZ Testbed$Loop ;No Data waiting
CALL CONIN ;Get data from buffer
CALL DB$Display ;Display character returned
DB DB$A ; in A register
DB 'CONIN returned',0
JMP Go$CONIN ;Repeat CONIN loop until no chars.
; waiting.
;
Go$CONOUT:
CALL CONST ;Get Console Status
JZ Testbed$Loop ;No data waiting
CALL CONIN
MOV C,A ;Ready for output
CALL CONOUT ;Output to Console
JMP Go$CONOUT ;Repeat while there is still data
;
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
Test$Stack:
;
; Dummy routines for those shown in other figures
;
; BIOS routines (Figure 8-10)
;
CONST: ;BIOS Console Status
CONIN: ;BIOS Console Input
CONOUT: ;BIOS Console Output;
Character$Interrupt: ;Interrupt service routine for incoming chars.
;
; Debug routines (Figure 10-2)
;
DB$Init: ;Debug initialization
DB$MSGI: ;Display message in-line
DB$CONINU: ;Get upper case character from keyboard
DB$Display: ;Main debug display routine
DB$A EQU 02 ;Display code for DB$Display


View File

@@ -0,0 +1,83 @@
; Figure 10-5
;
; Testbed for Real Time Clock driver in the BIOS.
;
; The complete source file consists of three components:
;
; 1. The testbed code shown here.
; 2. The Real Time Clock driver destined for the BIOS.
; 3. The debug package shown in Figure 10-2.
;
TRUE EQU 0FFFFH
FALSE EQU NOT TRUE
DEBUG EQU TRUE ;For conditional assembly of RST
; instructions in place of IN and
; OUT instructions in the drivers.
RST6 EQU 30H ;Use RST 6 for fake clock tick.
ORG 100H
START:
LXI SP,Test$Stack ;Use local stack
CALL DB$Init ;Initialize the debug package
MVI A,JMP ;Setup RST 6 with JMP opcode
STA RST6
LXI H,RTC$Interrupt ;Setup RST 6 JMP address
SHLD RST6 + 1
JMP Testbed$Loop ;<=== REMOVE THIS JMP WHEN READY TO
; TEST WATCHDOG ROUTINES
LXI B,50 ;50 ticks before timeout
LXI H,WD$Timeout ;Address to transfer to
CALL Set$Watchdog ;Set the watchdog timer
;
;
; Make repeated entry to RTC Interrupt routine
; to ensure that clock is correctly updated.
;
Testbed$Loop:
MVI A,0AAH ;Set registers to known pattern
LXI B,0BBCCH
LXI D,0DDEEH
LXI H,0FF11H
RST 6 ;Fake interrupt clock
CALL DB$MSGI ;Display in-line message
DB 'Clock =',0
LXI H,Time$In$ASCII ;Get address of clock in driver
CALL DB$MSG ;Display current clock value
; (Note : Time$In$ASCII already has
; a Line Feed character in it)
CALL DB$MSGI ;Display in-line message
DB 0DH,0 ;Carriage Return
JMP Testbed$Loop
;
; Control arrives here when the watchdog timer times
; out.
WD$Timeout:
CALL DB$MSGI
DB 0DH,0AH,'Watchdog timed out',0
RET ;Return to watchdog routine
;
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
Test$Stack:
;
; Dummy routines for those shown in other figures
;
; BIOS routines (Figure 8-10)
;
RTC$Interrupt: ;Interrupt service routine for clock tick
Set$Watchdog: ;Set Watchdog timer
Time$In$ASCII: ;ASCII string of HH:MM:SS, LF, 0
;
; Debug routines (Figure 10-2)
;
DB$Init: ;Debug initialization
DB$MSGI: ;Display message in-line
DB$MSG: ;Display message


View File

@@ -0,0 +1,168 @@
; Figure 10-6
;
; Testbed for Disk I/O drivers in the BIOS.
;
; The complete source file consists of three components:
;
; 1. The testbed code shown here.
; 2. The Disk I/O drivers destined for the BIOS.
; 3. The debug package shown in Figure 10-2.
;
TRUE EQU 0FFFFH
FALSE EQU NOT TRUE
DEBUG EQU TRUE ;For conditional assembly of RST
; instructions in place of IN and
; OUT instructions in the drivers.
ORG 100H
START:
LXI SP,Test$Stack ;Use a local stack
CALL DB$Init ;Initialize the debug package
;
; Make calls to SELDSK, SETTRK, SETSEC and SETDMA,
; then either a Read or Write routine.
;
Testbed$Loop:
LXI SP,Test$Stack ;Use local stack
LDA Logical$Disk ;Setup for SELDSK call
MOV C,A
CALL SELDSK
CALL DB$Display ;Display return value in HL
DB DB$HL
DB 'SELDSK returned',0
SHLD DPH$Start ;Setup to display Disk Parameter Header
LXI D,16 ;Compute end address
DAD D
SHLD DPH$End ;Store into debug call
CALL DB$Display ;Display DPH
DB DB$M ;Memory
DPH$Start:
DW 0
DPH$End:
DW 0
DB 'Selected DPH',0
LHLD Track ;Call SETTRK
PUSH H
POP B ;SETTRK needs track in BC
CALL SETTRK
LDA Sector ;Call SETSEC
MOV C,A ;SETSEC need sector in C
CALL SETSEC
LXI B,Test$Buffer ;Set DMA address
CALL SETDMA
LDA Write$Disk ;Check if reading or writing
ORA A
JNZ Test$Write
CALL Read$No$Deblock ;*** or Read$Physical depending on which
;*** drivers you are testing.
CALL DB$Display ;Display return code
DB DB$A
DB 'Test Read returned',0
CALL Check$Ripple ;Check if ripple pattern in buffer
JZ Testbed$Loop ;Yes, it is correct
CALL DB$MSGI ;Indicate problem
DB DB$HL ;Display HL (points to offending byte)
DB 'Ripple pattern incorrect. HL -> failure.',0
CALL DB$Display ;Display test buffer
CALL DB$M ;Memory
DW Test$Buffer
DW Test$Buffer$Size
DB 'Contents of Test$Buffer',0
JMP Testbed$Loop
Test$Write:
CALL Fill$Ripple ;Fill the test buffer with ripple pattern
CALL Write$No$Deblock;*** or Write$Physical depending on which
;*** drivers you are testing.
CALL DB$Display ;Display return code
DB DB$A
DB 'Test Write returned',0
JMP Testbed$Loop
Fill$Ripple: ;Fills the Test$Buffer with a pattern
; formed by putting into each byte, the
; least significant 8-bits of the byte's
; address.
LXI B,Test$Buffer$Size
LXI H,Test$Buffer
FR$Loop:
MOV M,L ;Set pattern value into buffer
INX H ;Update buffer pointer
DCX B ;Down date count
MOV A,C ;Check if count zero
ORA B
JNZ FR$Loop ;Repeat until zero
RET
;
Check$Ripple: ;Check that the buffer is filled with the
; correct ripple pattern.
; Returns with Zero status if this is true,
; non-zero status if the ripple is not
; correct. HL point to the offending byte
; (which should = L)
LXI B,Test$Buffer$Size
LXI H,Test$Buffer
CR$Loop:
MOV A,L ;Get correct value
CMP M ;Compare to that in the buffer
RNZ ;Mismatch - Non-zero already indicated
INX H ;Update buffer pointer
DCX B ;Down date count
MOV A,C ;Check count zero
ORA B
JNZ CR$Loop ;Repeat until zero
RET ;Zero flag will already be set
;
; Testbed variables
;
Logical$Disk: DB 0 ;A = 0, B = 1,...
Track: DW 0 ;Disk track number
Sector: DB 0 ;Disk sector number
Write$Disk: DB 0 ;NZ to write to disk
;
Test$Buffer$Size EQU 512 ;<=== Alter as required
Test$Buffer: DS Test$Buffer$Size
;
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
Test$Stack:
;
; Dummy routines for those shown in other figures
;
; BIOS routines (Figure 8-10)
;
SELDSK: ;Select logical disk
SETTRK: ;Set track number
SETSEC: ;Set sector number
SETDMA: ;Set DMA address
Read$No$Deblock: ;Driver read routines
Read$Physical:
Write$No$Deblock: ;Driver write routines
Write$Physical:
;
; Debug routines (Figure 10-2)
;
DB$Init: ;Debug initialization
DB$MSGI: ;Display message in-line
DB$Display: ;Main debug display routine
DB$A EQU 02 ;Display codes for DB$Display
DB$HL EQU 20
DB$M EQU 24


View File

@@ -0,0 +1,40 @@
; Figure 5-10
;
; IOBYTE Equates
; These are for accessing the IOBYTE.
;
; Mask Values to isolate specific devices.
; (These can also be inverted to preserve all BUT the
; specific device)
;
IO$CONM EQU 0000$0011B ;Console Mask
IO$RDRM EQU 0000$1100B ;Reader Mask
IO$PUNM EQU 0011$0000B ;Punch Mask
IO$LSTM EQU 1100$0000B ;List Mask
;
;Console Values
IO$CTTY EQU 0 ;Console -> TTY:
IO$CCRT EQU 1 ;Console -> CRT:
IO$CBAT EQU 2 ;Console Input <- RDR:
;Console Output -> LST:
IO$CUC1 EQU 3 ;Console -> UC1: (User console 1)
;
;Reader Values
IO$RTTY EQU 0 SHL 2 ;Reader <- TTY:
IO$RRDR EQU 1 SHL 2 ;Reader <- RDR:
IO$RUR1 EQU 2 SHL 2 ;Reader <- UR1: (User Reader 1)
IO$RUR2 EQU 3 SHL 2 ;Reader <- UR2: (user Reader 2)
;
;Punch Values
IO$PTTY EQU 0 SHL 4 ;Punch -> TTY:
IO$PPUN EQU 1 SHL 4 ;Punch -> PUN:
IO$PUP1 EQU 2 SHL 4 ;Punch -> UP1: (User Punch 1)
IO$PUP2 EQU 3 SHL 4 ;Punch -> UP2: (User Punch 2)
;
;List Values
IO$LTTY EQU 0 SHL 6 ;List -> TTY:
IO$LCRT EQU 1 SHL 6 ;List -> CRT:
IO$LLPT EQU 2 SHL 6 ;List -> LPT: (Physical Line Printer)
IO$LUL1 EQU 3 SHL 6 ;List -> UL1: (User List 1)
;


View File

@@ -0,0 +1,75 @@
; Figure 5-11
;
; This example shows how to use the Get and Set IOBYTE
; functions to implement a simple Terminal emulator.
; For this example to work, the BIOS must detect the
; Console Value being set to 3 (IO$CUC1) and connect
; Console Status, Input and Output functions to the
; Communications line.
;
B$DIRCONIO EQU 6 ;Direct Console Input/Output
B$GETIO EQU 7 ;Get IOBYTE
B$SETIO EQU 8 ;Set IOBYTE
B$CONST EQU 11 ;Get Console Status (sneak preview)
BDOS EQU 5 ;BDOS entry point
;
IO$CONM EQU 0000$0011B ;Console Mask for IOBYTE
IO$CCRT EQU 1 ;Console -> CRT:
IO$CUC1 EQU 3 ;Console -> User Console #1
;
TERM:
CALL SETCRT ;Connect Console -> CRT:
TERM$CKS:
CALL CONST ;Get CRT status
JZ TERM$NOKI ;No console input
CALL CONIN ;Get keyboard character
CALL SETCOMM ;Connect Console -> Comm. Line
CALL CONOUT ;Output to Comm. Line
TERM$CCS: ;Check Comm. Status
CALL CONST ;Get "Console" status
JZ TERM ;No incoming Comm. character
CALL CONIN ;Get incoming Comm. character
CALL SETCRT ;Connect Console -> CRT:
CALL CONOUT ;Output to CRT
JMP TERM$CKS ;Loop back to check keyboard status
TERM$NOKI:
CALL SETCOMM ;Connect Console -> Comm. Line
JMP TERM$CCS ;Loop back to check Comm. status
;
SETCRT: ;Connect Console -> CRT:
PUSH PSW ;Save possible data character
MVI B,IO$CCRT ;Connect Console -> CRT:
JMP SETCON ;Common Code
SETCOMM: ;Connect Console -> Comm. Line
PUSH PSW ;Save possible data character
MVI B,IO$CUC1 ;Connect Console -> Comm. Line
;Drop into SETCON
SETCON: ;Set Console Device
;New code in B (in bits 1,0)
PUSH B ;Save code
MVI C,B$GETIO ;Get current IOBYTE
CALL BDOS
ANI (NOT IO$CONM) AND 0FFH ;Preserve all but console
POP B ;Recover required code
ORA B ;OR in new bits
MOV E,A ;Ready for setting
MVI C,B$SETIO ;Function code
CALL BDOS
POP PSW ;Recover possible data character
RET
CONOUT:
MOV E,A ;Get data byte for output
MVI C,B$DIRCONIO ;Function code
JMP BDOS ;BDOS returns to CONOUT's caller
CONIN:
MVI C,B$DIRCONIO ;Function Code
MVI E,0FFH ;Indicate Console Input
JMP BDOS ;BDOS returns to CONIN's caller
CONST:
MVI C,B$CONST ;Function Code
CALL BDOS
ORA A ;Set Z-flag to result
RET


View File

@@ -0,0 +1,79 @@
; Figure 5-12
;
; OM
; Output Message
; This subroutine selects one of several messages based on
; the contents of the A register on entry. It then displays
; this message on the console.
;
; Each message is declared with a '$' as its last character.
; If the A register contains a value larger than the number
; of messages declared, OM will output "Unknown Message".
;
; As an option, OM can output Carriage Return / Line Feed
; prior to outputting the message text.
;
; Entry Parameters
;
; HL -> Message Table
; This has the form :
; DB 3 ;Number of Messages in table
; DW MSG0 ;Address of text (A = 0)
; DW MSG1 ;(A = 1)
; DW MSG2 ;(A = 2)
;
; MSG0: DB 'Message text$'
; ...etc.
;
; A = Message Code (from 0 on up)
; B = Output CR/LF if Non-zero
;
; Calling sequence
;
; LXI H,MSG$TABLE
; LDA MSGCODE
; MVI B,0 ;Suppress CR/LF
; CALL OM
;
B$PRINTS EQU 9 ;Print $-terminated string
BDOS EQU 5 ;BDOS Entry Point
;
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
OM$CRLF: DB CR,LF,'$'
OM$UM: DB 'Unknown Message$'
;
;
OM:
PUSH PSW ;Save message code
PUSH H ;Save message table pointer
MOV A,B ;Check if CR/LF required
ORA A
JZ OM$NOCR ;No
LXI D,OM$CRLF ;Output CR/LF
MVI C,B$PRINTS
CALL BDOS
OM$NOCR:
POP H ;Recover message table pointer
POP PSW ;Recover message code
CMP M ;Compare message to max. value
JNC OM$ERR ;Error - code not <= max.
INX H ;Bypass max. value in table
ADD A ;Message Code * 2
MOV E,A ;Make Code * 2 a word value
MVI D,0
DAD D ;HL -> Address of message text
MOV E,M ;Get LS Byte
INX H ;HL -> MS Byte
MOV D,M ;Get MS Byte
;DE -> Message text itself
OM$PS: ;Print string entry point
MVI C,B$PRINTS ;Function Code
CALL BDOS
RET ;Return to caller
;
OM$ERR: ;Error
LXI D,OM$UM ;Point to "Unknown Message"
JMP OM$PS ;Print string


View File

@@ -0,0 +1,159 @@
; Figure 5-13
;
; RSA
; Return Subprocessor Address
; This subroutine returns one of several addresses selected
; from a table by matching keyboard input against
; specified strings. It is normally used to switch control
; to a particular subprocessor according to an option entered
; by the operator from the keyboard.
;
; Character string comparisons are performed with case-folding,
; that is lower-case letters are converted to upper-case.
;
; If the operator input fails to match any of the specified
; strings, then the Carry flag is set, otherwise it is cleared.
;
; Entry Parameters
;
; HL -> Subprocessor Select Table
; This has the form :
; DW TEXT0,SUBPROC0
; DW TEXT1,SUBPROC1
; DW 0 ;Terminator
;
; TEXT0: DB 'add',0 ;00H-byte terminated
; TEXT1: DB 'subtract',0
;
; SUBPROC0:
; Code for processing ADD function.
; SUBPROC1:
; Code for processing SUBTRACT function.
;
; Exit Parameters
;
; DE -> operator input string (00H-terminated input string).
; Carry Clear, HL -> Subprocessor.
; Carry Set, HL = 0000H.
;
; Calling Sequence
;
; LXI H,SUBPROCTAB ;Subprocessor Table
; CALL RSA
; JC ERROR ;Carry set only on error
; LXI D,RETURN ;Fake CALL instruction
; PUSH D ;Push Return address on stack
; PCHL ;"CALL" to Subprocessor
; RETURN:
;
B$READCONS EQU 10 ;Read Console String into Buffer
BDOS EQU 5 ;BDOS Entry Point
;
RSA$BL EQU 80 ;Buffer Length
RSA$BUF: DB RSA$BL ;Max. no. of characters
RSA$ACTC: DB 0 ;Actual no. of characters
RSA$BUFC: DS RSA$BL ;Buffer characters
DB 0 ;Safety terminator
;
;
RSA:
DCX H ;Adjust Subproc. pointer for code below
DCX H
PUSH H ;Top of Stack (TOS) -> Subproc. table - 2
MVI C,B$READCONS ;Function Code
LXI D,RSA$BUF ;DE -> Buffer
CALL BDOS ;Read operator input
;Convert input to 00H-terminated
LXI H,RSA$ACTC ;HL -> Actual no. of chars input
MOV E,M ;Get Actual no. of chars input
MVI D,0 ;Make into word value
INX H ;HL -> first data character
DAD D ;HL -> first UNUSED character in buffer
MVI M,0 ;Make input buffer 00H-terminated
;Compare input to specified values
;Main loop
RSA$ML:
POP H ;Recover subprocessor table pointer
INX H ;Move to top of next entry
INX H ;HL -> Text Address
MOV E,M ;Get text address
INX H
MOV D,M ;DE -> text
MOV A,D ;Check if at end of subproc. table
ORA E
JZ RSA$NFND ;Match not Found
INX H ;HL -> subprocessor address
PUSH H ;Save ptr to subprocessor table
LXI H,RSA$BUFC ;HL -> input characters
CALL FSCMP ;Folded string compare
JNZ RSA$ML ;No match, move to next entry
POP H ;Match found, recover subproc. ptr
MOV E,M ;Get actual subprocessor address
INX H
MOV D,M ;DE -> Subprocessor code
XCHG ;HL -> Subprocessor code
ORA A ;Clear carry (match found)
RET
;
RSA$NFND:
LXI H,0 ;Indicate no match found
STC ;Set carry
RET
;
;
; FSCMP
; Folded (lower case to upper) string compare.
; This subroutine compares two 00H-byte terminated
; strings and returns with the condition flags set
; to indicate their relationship.
;
; Entry parameters
;
; DE -> String 1
; HL -> String 2
;
; Exit parameters
;
; Flags set (based on String1 - String2, on a
; character-by-character basis)
;
;
FSCMP:
LDAX D ;Get String 1 character
CALL FOLD ;Fold to upper case
PUSH PSW ;Save String 1 character
MOV A,M ;Get String 2 character
CALL FOLD ;Fold to upper case
MOV B,A ;Save String 2 character
POP PSW ;Recover String 1 character
CMP B ;String 1 - String 2
RNZ ;Return if not equal
ORA A ;Equal, so check if end of strings
RZ ;Yes
INX D ;No, update String 1 pointer
INX H ; and String 2 pointer
JMP FSCMP ;Check next character
;
;
; FOLD
; Folds a lower case letter (a-z) to UPPER CASE (A-Z)
;
; The character to be folded is in A on entry and on exit.
;
FOLD:
MOV C,A ;Preserve input character
MVI A,'a'-1 ;Check if folding needed
CMP C ;Compare to input character
JNC FOLDX ;No, char. is <= 'a'
MVI A,'z' ;Check if < 'z'
CMP C
JC FOLDX ;No, char. is > 'z'
MVI A,0DFH ;Fold character
ANA C
RET
FOLDX:
MOV A,C ;Recover original input char.
RET


View File

@@ -0,0 +1,46 @@
; Figure 5-14
;
; CCPM
; Check if CP/M
; This subroutine determines the version number of the
; Operating System and, if not CP/M version 2, displays
; an error message and executes a warm boot.
;
; Entry and Exit Parameters
;
; None
;
; Calling sequence
;
; CALL CCPM ;Warm boots if not CP/M 2
;
B$PRINTS EQU 9 ;Display $-terminated string
B$GETVER EQU 12 ;Get version number
BDOS EQU 5 ;BDOS Entry Point
;
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
CCPMM: DB CR,LF
DB 'This program can only run under CP/M version 2.'
DB CR,LF,'$'
;
;
CCPM:
MVI C,B$GETVER ;Get version number
CALL BDOS
MOV A,H ;H must be 0 for CP/M
ORA A
JNZ CCPME ;Must be MP/M
MOV A,L ;L = version number of CP/M
ANI 0F0H ;Version number in MS Nibble
CPI 20H ;Check if version 2
JNZ CCPME ;Must be an earlier version
RET ;Yes, we under CP/M version 2
;
CCPME: ;Error
MVI C,B$PRINTS ;Display error message
LXI D,CCPMM
CALL BDOS
JMP 0 ;Warm Boot


View File

@@ -0,0 +1,50 @@
; Figure 5-15
;
; CDISK
; Change Disk
; This subroutine displays a message requesting the
; to change the specified logical disk, then waits for
; a Carriage Return to be pressed. It then issues
; a Disk Reset and returns to the caller.
;
; Entry Parameters
;
; A = Logical disk to be changed (A = 0, B = 1)
;
; Exit Parameters
;
; None
;
; Calling Sequence
;
; MVI A,0 ;Change drive A:
; CALL CDISK
;
;
B$DSKRESET EQU 13 ;Disk Reset function code
B$PRINTS EQU 9 ;Print $-terminated string
B$CONIN EQU 1 ;Get console input
BDOS EQU 5 ;BDOS Entry Point
;
CR EQU 0DH
LF EQU 0AH
;
CDISKM: DB CR,LF,'Change logical disk '
CDISKD: DB 0
DB ': and press Carriage Return to continue$'
;
CDISK:
ADI 'A'-1 ;Convert to Letter
STA CDISKD ;Store into message
MVI C,B$PRINTS ;Display Message
LXI D,CDISKM
CALL BDOS
CDISKW:
MVI C,B$CONIN ;Get keyboard character
CALL BDOS
CPI CR
JNZ CDISKW
MVI C,B$DSKRESET ;Now reset disk system
CALL BDOS
RET


View File

@@ -0,0 +1,176 @@
; Figure 5-16
;
; OPENF
; Open File
;
; Given a pointer to a 00H-byte terminated file name,
; and an area that can be used for a File Control Block,
; this subroutine builds a valid File Control Block, and
; attempts to open the File.
;
; If the file is opened, it returns with Carry Flag clear.
; If the file cannot be opened, this subroutine returns
; with the Carry Flag set.
;
; Entry Parameters
;
; DE -> 36-byte area for File Control Block
; HL -> 00H-byte terminated file name of the form
; {disk:} Name {.typ}
; (That is, the disk and typ are optional)
;
; Exit Parameters
;
; Carry Clear : File opened correctly.
; Carry Set : File not opened.
;
; Calling Sequence
;
; LXI D,FCB
; LXI H,FNAME
; CALL OPENF
; JC ERROR
;
; where
;
; FCB: DS 36 ;Space for File Control Block
; FNAME: DB 'A:TESTFILE.DAT',0
;
;
B$OPEN EQU 15 ;File Open Function Code
BDOS EQU 5 ;BDOS Entry Point
;
;
OPENF:
PUSH D ;Preserve pointer to FCB
CALL BF ;Build File Control Block (FCB)
MVI C,B$OPEN
POP D ;Recover Pointer to FCB
CALL BDOS
RAL ;If A=0FFH, Carry set
;otherwise Carry Clear
RET
;
; BF
; Build File Control Block
; This subroutine formats a 00H-byte terminated string
; (presumed to be a File Name) into an FCB, setting the
; Disk, File name and Type and clearing the remainder of the
; FCB to 0's.
;
; Entry Parameters
;
; DE -> File Control Block (36 Bytes)
; HL -> File Name String (00H-byte terminated)
;
; Exit Parameters
;
; The built File Control block.
;
; Calling Sequence
;
; LXI D,FCB
; LXI H,FILENAME
; CALL BF
;
;
BF:
INX H ;Check if 2nd char. is ":"
MOV A,M ;Get character from file name
DCX H ;HL -> now back at 1st char
CPI ':' ;If ":", then Disk specified
JNZ BF$ND ;No disk
MOV A,M ;Get disk letter
ANI 0001$1111B ;A (41H) -> 1, B (42H) -> 2 ...
INX H ;Bypass disk letter
INX H ;Bypass ":"
JMP BF$SD ;Store disk in FCB
BF$ND: ;No disk present
XRA A ;Indicate default disk
BF$SD:
STAX D ;Store disk in FCB
INX D ;DE -> 1st char. of name in FCB
MVI C,8 ;File name length
CALL BF$GT ;Get token
;Note - at this point, BF$GT will
;have advanced the string pointer to
;either a '.' or 00H byte
CPI '.' ;Check terminating character
JNZ BF$NT ;No file type specified
INX H ;Bypass '.' in file name
BF$NT:
MVI C,3 ;File Type Length
CALL BF$GT ;Get Token
;Note - if no file type is present
;BF$GT will merely spacefill the FCB
MVI B,0 ;0-fill the remainder of the FCB
MVI C,24 ;36 - 12 (Disk, Name, Type = 12 chars.)
CALL BF$FT ;Re-use Fill Token S/R
RET
;
; BF$GT
; Build FCB - Get Token
;
; This subroutine scans a file name string,
; placing characters into a File Control Block.
; On encountering a terminator character ("." or 00H),
; the remainder of the token is space filled.
; If an "*" is encountered, the remainder of the token
; is filled with "?".
;
; Entry Parameters
;
; DE -> Into File Control Block
; HL -> Into File Name String
; C = Maximum no. of characters in token
;
; Exit Parameters
;
; File Control Block contains next token.
; A = Terminating Character.
BF$GT:
MOV A,M ;Get next string character
ORA A ;Check if end of string
JZ BF$SFT ;Yes, space fill token
CPI '*' ;Check if ?-fill required
JZ BF$QFT ;Yes, fill with ?
CPI '.' ;Assume current token is Filename
;check if file type coming up
;(If current token is File type this
;check is benignly redundant)
JZ BF$SFT ;Yes, space fill token
STAX D ;None of the above, so store in FCB
INX D ;Update FCB Pointer
INX H ;Update String Pointer
DCR C ;Countdown on token length
JNZ BF$GT ;Still more characters to go
BF$SKIP: ;Skip chars. until '.' or 00H
MOV A,M ;Get next string character
ORA A ;Check if 00H
RZ ;Yes
CPI '.' ;Check if '.'
RZ ;Yes
INX H ;Update string pointer (only)
JMP BF$SKIP ;Try next character
BF$SFT: ;Space fill token
MVI B,' '
JMP BF$FT ;Common Fill Token code
;BF$FT returns to caller
BF$QFT: ;Question Mark Fill Token
MVI B,'?'
CALL BF$FT ;Common Fill Token Code
JMP BF$SKIP ;Bypass multiple '*' etc.
BF$FT: ;Fill Token
PUSH PSW ;Save terminating character
MOV A,B ;Get fill characer
BF$FTL: ;Inner loop
STAX D ;Store in FCB
INX D ;Update FCB Pointer
DCR C ;Downdate residual count
JNZ BF$FTL ;Keep going
POP PSW ;Recover terminating character
RET


View File

@@ -0,0 +1,189 @@
; Figure 5-17
;
; GNF
; This subroutine returns an FCB setup with either the first
; file matched by an ambiguous file name, or (if specified
; by entry parameter) the next file name.
;
; Note : this subroutine is context sensitive. You must not
; have more than one ambiguous file name sequence in
; process at any given time.
;
;>>> Warning : This subroutine changes the DMA Address inside
;>>> the BDOS.
;
; Entry Parameters
;
; DE -> Possibly Ambiguous File Name
; (00-byte terminated)
; (Only needed for FIRST request)
; HL -> File Control Block
; A = 0 : Return FIRST file name that matches
; = NZ : Return NEXT file name that matches
;
; Exit Parameters
;
; Carry set : A = FF, no file name matches
; A not = 0FFH, error in input file name
; Carry clear : FCB setup with next name
; HL -> Directory Entry returned by
; Search First/Next
;
; Calling sequence
;
; LXI D,FILENAME
; LXI H,FCB
; MVI A,0 ;or MVI A,1 for NEXT
; CALL GNF
;
B$SEARCHF EQU 17 ;Search for First file name
B$SEARCHN EQU 18 ;Search for Next file name
B$SETDMA EQU 26 ;Setup DMA Address
BDOS EQU 5 ;BDOS Entry Point
;
GNFDMA EQU 80H ;Default DMA Address
GNFSVL EQU 13 ;Save Length (no. of chars to move)
GNFFCL EQU 36 ;File Control Block length
GNFSV: DS GNFSVL ;Save area for file name/type
;
GNF:
PUSH H ;Save FCB pointer
PUSH D ;Save File Name pointer
PUSH PSW ;Save First/Next flag
LXI D,GNFDMA ;Set DMA to known address
MVI C,B$SETDMA ;Function code
CALL BDOS
POP PSW ;Recover First/Next flag
POP H ;Recover File Name pointer
POP D ;Recover FCB Pointer
PUSH D ;Re-save FCB Pointer
ORA A ;Check if FIRST or NEXT
JNZ GNFN ;NEXT
CALL BF ;Build File Control BLock
POP H ;Recover FCB Pointer (to balance stack)
RC ;Return if error in File Name
PUSH H ;Resave FCB pointer
;Move Ambiguous File Name to save area
;HL -> FCB
LXI D,GNFSV ;DE -> save area
MVI C,GNFSVL ;Get save length
CALL MOVE
POP D ;Recover FCB Pointer
PUSH D ;and re-save
MVI C,B$SEARCHF ;Search FIRST
CALL BDOS
POP H ;Recover FCB Pointer
CPI 0FFH ;Check for error
JZ GNFEX ;Error exit
JMP GNFC ;Common code
;
GNFN:
;Execute search FIRST to re-establish
;contact with previous file
;User's FCB still has Name/Type in it
CALL GNFZF ;Zero-fill all but File Name/Type
POP D ;Recover FCB address
PUSH D ;and re-save
MVI C,B$SEARCHF ;Re-find the file
CALL BDOS
POP D ;Recover FCB Pointer
PUSH D ;and re-save
LXI H,GNFSV ;Move File Name from save area into FCB
MVI C,GNFSVL ;Save area length
CALL MOVE
MVI C,B$SEARCHN ;Search NEXT
CALL BDOS
POP H ;Recover FCB address
CPI 0FFH ;Check for error
JZ GNFEX ;Error exit
GNFC:
PUSH H ;Save FCB Address
ADD A ;Multiply BDOS Return Code * 32
ADD A ;* 4
ADD A ;* 8
ADD A ;* 16
ADD A ;* 32
LXI H,GNFDMA ;HL -> DMA Address
MOV E,A ;Make Code * 32 a word value in DE
MVI D,0
DAD D ;HL -> File's directory entry
;Move File Name into FCB
POP D ;Recover FCB Address
PUSH H ;Save Directory Entry pointer
PUSH D ;and re-save
MVI C,GNFSVL ;Length of save area
CALL MOVE
LDA GNFSV ;Get Disk Drive from save area
POP D ;Recover FCB Address
STAX D ;Overwrite user number in FCB
;Setup to zero-fill tail end of FCB
CALL GNFZF ;Zero-fill
POP H ;Recover Directory Entry Pointer
XRA A ;Clear Carry
RET
;
GNFEX:
STC ;Set Carry to indicate error
RET
;
; GNFZF
; Get Next File - Zero Fill
; This subroutine Zero-fills the rest of the bytes in an FCB
; that follow the File Name and Type.
;
; Entry Parameters
;
; DE -> File Control Block
;
GNFZF:
LXI H,GNFSVL ;Bypass area that holds file name
DAD D ;HL -> FCB + GNFSVL
MOV D,H ;DE -> FCB + GNFSVL
MOV E,L
INX D ;DE -> FCB + GNFSVL + 1
MVI M,0 ;FCB + GNFSVL = 0
MVI C,GNFFCL-GNFSVL ;Remainder of File Control Block
; Drop into MOVE ;Spread 0's through remainder of FCB
; v
;
; MOVE
; This subroutine moves C bytes from (HL) to (DE).
;
MOVE:
MOV A,M ;Get source byte
STAX D ;Save destination byte
INX D ;Increment destination pointer
INX H ;Increment source pointer
DCR C ;Downdate count
JNZ MOVE ;Go back for more
RET
;
; BF
; Build File Control Block
;
; This subroutine formats a 00H-byte terminated string
; (presumed to be a File Name) into an FCB, setting the
; Disk, File name and Type and clearing the remainder of the
; FCB to 0's.
;
; Entry Parameters
;
; DE -> File Control Block (36 Bytes)
; HL -> File Name String (00H-byte terminated)
;
; Exit Parameters
;
; The built File Control block.
;
; This subroutine is shown in full in Figure 5.32
;
BF: RET ;Dummy subroutine for this example


View File

@@ -0,0 +1,73 @@
; Figure 5-18
;
; GETC
; This subroutine gets the next character from a
; Sequential disk file. It assumes that the file has
; already been opened.
;
;>>> Note : this subroutine changes CP/M's DMA Address.
;
; Entry Parameters
;
; DE -> File Control Block.
;
; Exit Parameters
;
; A = next character from file.
; (= 0FFH on physical End of File).
; Note : 1AH is normal EOF character for
; ASCII Files.
;
; Calling sequence
;
; LXI DE,FCB
; CALL GETC
; CPI 1AH
; JZ EOFCHAR
; CPI 0FFH
; JZ ACTUALEOF
;
B$READSEQ EQU 20 ;Read Sequential
B$SETDMA EQU 26 ;Set DMA Address
BDOS EQU 5 ;BDOS Entry Point
;
GETCBS EQU 128 ;Buffer Size
GETCBF: DS GETCBS ;Declare buffer
GETCCC: DB 0 ;Char. count (initially 'empty')
;
GETC:
LDA GETCCC ;Check if buffer is empty
ORA A
JZ GETCFB ;Yes, fill buffer
GETCRE: ;Re-entry point after buffer filled
DCR A ;No, downdate count
STA GETCCC ;Save downdated count
MOV B,A ;Compute offset of next character
MVI A,GETCBS-1 ;By subtracting
SUB B ;(buffer size - downdated count)
MOV E,A ;Make result into word value
MVI D,0
LXI H,GETCBF ;HL -> Base of Buffer
DAD D ;HL -> Next character in buffer
MOV A,M ;Get next character
RET
;
GETCFB: ;Fill Buffer
PUSH D ;Save FCB Pointer
LXI D,GETCBF ;Set DMA Address to Buffer
MVI C,B$SETDMA ;Function Code
CALL BDOS
POP D ;Recover FCB Pointer
MVI C,B$READSEQ ;Read Sequential 'record' (sector)
CALL BDOS
ORA A ;Check if read unsuccessful (A = NZ)
JNZ GETCX ;Yes
MVI A,GETCBS ;Reset count
STA GETCCC
JMP GETCRE ;Re-enter subroutine
;
GETCX: ;Physical end of file
MVI A,0FFH ;Indicate such
RET


View File

@@ -0,0 +1,95 @@
; Figure 5-19
;
; PUTC
; This subroutine either puts the next chararacter out
; to a sequential file, writing out completed 'records'
; (128-byte sectors) or, if requested to, will fill the
; remainder of the current 'record' with 1AH's to indicate
; End of File to CP/M.
;
; Entry Parameters
;
; DE -> File Control Block
; B = 0, A = next data character to be output
; B /= 0, fill the current 'record' with 1AH's
;
; Exit Parameters
;
; None.
;
; Calling sequence
;
; LXI D,FCB
; MVI B,0 ;Not end of File
; LDA CHAR
; CALL PUTC
; ..or
; LXI D,FCB
; MVI B,1 ;Indicate end of file
; CALL PUTC
;
B$WRITESEQ EQU 21 ;Write Sequential
B$SETDMA EQU 26 ;Set DMA Address
BDOS EQU 5 ;BDOS Entry Point
;
PUTCBS EQU 128 ;Buffer Size
PUTCBF: DS PUTCBS ;Declare buffer
PUTCCC: DB 0 ;Char. count (initially 'empty')
;
PUTC:
PUSH D ;Save FCB Address
PUSH PSW ;Save data character
MOV A,B ;Check if end of file requested
ORA A
JNZ PUTCEF ;Yes
CALL PUTCGA ;No, get address of next free byte
;HL -> next free byte
;E = Current Char. count (as well as A)
POP PSW ;Recover data character
MOV M,A ;Save in buffer
MOV A,E ;Get current character count
INR A ;Update character count
CPI PUTCBS ;Check if buffer full
JZ PUTCWB ;Yes, write buffer
STA PUTCCC ;No, save updated count
POP D ;Dump FCB Address for return
RET
;
PUTCEF: ;End of file
POP PSW ;Dump data character
CALL PUTCGA ;HL -> next free byte
;A = Current character count
PUTCCE: ;Copy EOF character
CPI PUTCBS ;Check for end of buffer
JZ PUTCWB ;Yes, write out the buffer
MVI M,1AH ;No, store EOF in buffer
INR A ;Update count
INX H ;Update buffer pointer
JMP PUTCCE ;Continue until end of buffer
;
PUTCWB: ;Write buffer
XRA A ;Reset character count to 0
STA PUTCCC
LXI D,PUTCBF ;DE -> Buffer
MVI C,B$SETDMA ;Set DMA Address -> Buffer
CALL BDOS
POP D ;Recover FCB Address
MVI C,B$WRITESEQ ;Write Sequential Record
CALL BDOS
ORA A ;Check if error
JNZ PUTCX ;Yes if A = NZ
RET ;No, return to caller
;
PUTCX: ;Error exit
MVI A,0FFH ;Indicate such
RET
;
PUTCGA: ;Return with HL -> next free char.
;and A = current char. count
LDA PUTCCC ;Get current character count
MOV E,A ;Make word value in DE
MVI D,0
LXI H,PUTCBF ;HL -> Base of buffer
DAD D ;HL -> next free character
RET


View File

@@ -0,0 +1,47 @@
; Figure 5-2
;
; EQUates for BDOS Function Code numbers.
;
B$SYSRESET EQU 0 ;System Reset
B$CONIN EQU 1 ;Read Console Byte
B$CONOUT EQU 2 ;Write Console Byte
B$READIN EQU 3 ;Read "Reader" Byte
B$PUNOUT EQU 4 ;Write "Punch" Byte
B$LISTOUT EQU 5 ;Write Printer Byte
B$DIRCONIO EQU 6 ;Direct Console I/O
B$GETIO EQU 7 ;Get IOBYTE
B$SETIO EQU 8 ;Set IOBYTE
B$PRINTS EQU 9 ;Print String
B$READCONS EQU 10 ;Read Console String
B$CONST EQU 11 ;Read Console Status
B$GETVER EQU 12 ;Get CP/M Version Number
B$DSKRESET EQU 13 ;Disk System Reset
B$SELDSK EQU 14 ;Select Disk
B$OPEN EQU 15 ;Open File
B$CLOSE EQU 16 ;Close File
B$SEARCHF EQU 17 ;Search for First Name Match
B$SEARCHN EQU 18 ;Search for Next Name Match
B$ERASE EQU 19 ;Erase (delete) File
B$READSEQ EQU 20 ;Read Sequential
B$WRITESEQ EQU 21 ;Write Sequential
B$CREATE EQU 22 ;Create File
B$RENAME EQU 23 ;Rename File
B$GETACTDSK EQU 24 ;Get Active (Logged-in) Disks
B$GETCURDSK EQU 25 ;Get Current Default Disk
B$SETDMA EQU 26 ;Set DMA (Read/Write) Address
B$GETALVEC EQU 27 ;Get Allocation Vector Address
B$SETDSKRO EQU 28 ;Set Disk to Read Only
B$GETRODSKS EQU 29 ;Get Read Only Disks
B$SETFAT EQU 30 ;Set File Attributes
B$GETDPB EQU 31 ;Get Disk Parameter Block Address
B$SETGETUN EQU 32 ;Set/Get User Number
B$READRAN EQU 33 ;Read Random
B$WRITERAN EQU 34 ;Write Random
B$GETFSIZ EQU 35 ;Get File Size
B$SETRANREC EQU 36 ;Set Random Record number
B$RESETD EQU 37 ;Reset Drive
B$WRITERANZ EQU 40 ;Write Random with Zero Fill
;
BDOS EQU 5 ;BDOS Entry Point


View File

@@ -0,0 +1,38 @@
; Figure 5-20
;
; CF
; Create File
; This subroutine creates a file. It erases any previous
; File before creating the new one.
;
; Entry Parameters
;
; DE -> File Control Block for new file
;
; Exit Parameters
;
; Carry Clear if operation successful (A = 0,1,2,3)
; Carry Set if error (A = 0FFH)
;
; Calling Sequence
;
; LXI D,FCB
; CALL CF
; JC ERROR
;
B$ERASE EQU 19 ;Erase File
B$CREATE EQU 22 ;Create File
BDOS EQU 5 ;BDOS Entry Point
;
;
CF:
PUSH D ;Preserve FCB Pointer
MVI C,B$ERASE ;Erase any existing file
CALL BDOS
POP D ;Recover FCB Pointer
MVI C,B$CREATE ;Create (and open new file)
CALL BDOS
CPI 0FFH ;Carry set if OK, Clear if Error
CMC ;Complete to use Carry set if Error
RET


View File

@@ -0,0 +1,84 @@
; Figure 5-21
;
; RF
; Rename File
; This subroutine renames a file.
; It uses the BF (Build FCB) subroutine shown in Figure 5.32
;
; Entry Parameters
;
; *** No case-folding of file names occurs ***
; HL -> Old File Name (00-byte terminated)
; DE -> New File Name (00-byte terminated)
;
; Exit Parameters
;
; Carry clear if operation successful (A = 0,1,2,3)
; Carry set if error (A =0FFH)
;
; Calling sequence
;
; LXI H,OLDNAME ;HL -> Old Name
; LXI D,NEWNAME ;DE -> New Name
; CALL RF
; JC ERROR
;
B$OPEN EQU 15 ;Open File
B$RENAME EQU 23 ;Rename File
BDOS EQU 5 ;BDOS Entry Point
;
RFFCB: DW 0,0,0,0,0,0,0,0,0 ;1 1/2 FCB's long
DW 0,0,0,0,0,0,0,0,0
DW 0,0,0,0,0,0,0,0,0
;
;
RF:
PUSH D ;Save New Name pointer
LXI D,RFFCB ;Build Old Name FCB
;HL already -> Old Name
CALL BF
POP H ;Recover New Name pointer
LXI D,RFFCB+16 ;Build new name in second part
CALL BF ;of File Control Block
LXI D,RFFCB+16 ;Experimentally try
MVI C,B$OPEN ;to Open the new File
CALL BDOS ;to ensure it does not
CPI 0FFH ;already exist
MVI A,0FEH ;Assume error (flags unchanged)
RC ;Carry set if A was 0,1,2,3
LXI D,RFFCB ;Rename the file
MVI C,B$RENAME
CALL BDOS
CPI 0FFH ;Carry set if OK, clear if error
CMC ;Invert to use Carry set if error
RET
;
; BF
; Build File Control Block
; This subroutine formats a 00H-byte terminated string
; (presumed to be a File Name) into an FCB, setting the
; Disk, File name and Type and clearing the remainder of the
; FCB to 0's.
;
; Entry Parameters
;
; DE -> File Control Block (36 Bytes)
; HL -> File Name String (00H-byte terminated)
;
; Exit Parameters
;
; The built File Control block.
;
; Calling Sequence
;
; LXI D,FCB
; LXI H,FILENAME
; CALL BF
;
;
BF:
RET ;Dummy subroutine : see Figure 5.32.


View File

@@ -0,0 +1,79 @@
; Figure 5-22
;
; SFA
; Set File Attributes
; This subroutine takes a compressed bit map of all the
; File Attribute bits, expands them out into an existing
; File Control Block and then requests CP/M to set the
; Attributes in the File Directory.
;
; Entry Parameters
;
; DE -> File Control Block
; HL = Bit Map. Only the most significant 11 bits
; are used, and correspond directly with the
; possible attribute bytes.
;
; Exit Parameters
;
; Carry Clear if operation successful (A = 0,1,2,3)
; Carry Set if error (A = 0FFH)
;
; Calling Sequence
;
; LXI D,FCB
; LXI H,0000$0000$1100$0000B ;Bit Map
; CALL SFA
; JC ERROR
;
; File Attribute Equates
;
FA$F1 EQU 1000$0000$0000$0000B ;F1' )
FA$F2 EQU 0100$0000$0000$0000B ;F2' ) Available for use
FA$F3 EQU 0010$0000$0000$0000B ;F3' ) by Application Programs
FA$F4 EQU 0001$0000$0000$0000B ;F4' )
FA$F5 EQU 0000$1000$0000$0000B ;F5' ]
FA$F6 EQU 0000$0100$0000$0000B ;F6' ] Reserved for CP/M
FA$F7 EQU 0000$0010$0000$0000B ;F7' ]
FA$F8 EQU 0000$0001$0000$0000B ;F8' ]
;
FA$T1 EQU 0000$0000$1000$0000B ;T1' - Read/Only File
FA$RO EQU FA$T1
FA$T2 EQU 0000$0000$0100$0000B ;T2' - System Files
FA$SYS EQU FA$T2
FA$T3 EQU 0000$0000$0010$0000B ;T3' - Reserved for CP/M
;
;
B$SETFAT EQU 30 ;Set File Attributes
BDOS EQU 5 ;BDOS Entry Point
;
;
SFA:
PUSH D ;Save FCB Pointer
INX D ;HL -> 1st character of file name
MVI C,8+3 ;Loop Count for File Name and Type
SFAL: ;Main Processing Loop
XRA A ;Clear Carry and A
DAD H ;Shift next MS Bit into Carry
ACI 0 ;A = 0 or 1 depending on Carry
RRC ;Rotate LS bit of A into MS bit
MOV B,A ;Save result (00H or 80H)
XCHG ;HL -> FCB character
MOV A,M ;Get FCB character
ANI 7FH ;Isolate all but Attribute bit
ORA B ;Set Attribute with result
MOV M,A ;and store back into FCB
XCHG ;DE -> FCB, HL = remaining bit map
INX D ;DE -> Next character in FCB
DCR C ;Downdate character count
JNZ SFAL ;Loop back for next character
MVI C,B$SETFAT ;Set File Attribute function code
POP D ;Recover FCB Pointer
CALL BDOS
CPI 0FFH ;Carry set if OK, clear if error
CMC ;Invert to use Carry set if error
RET


View File

@@ -0,0 +1,34 @@
; Figure 5-3
;
; Useful subroutines using the Write Console Byte
; BDOS Function.
;
; MSGOUT
; Output null-byte terminated message
;
; Calling sequence
;
; MESSAGE: DB 'Message',0
; :
; LXI H,MESSAGE
; CALL MSGOUT
;
; Exit Parameters
; HL -> Null byte terminator
;
B$CONOUT EQU 2 ;Write console byte
BDOS EQU 5 ;BDOS Entry Point
;
MSGOUT:
MOV A,M ;Get next byte for output
ORA A
RZ ;Return when null-byte
INX H ;Update message pointer
PUSH H ;Save updated pointer
MOV E,A ;Ready for BDOS
MVI C,B$CONOUT ;Function code
CALL BDOS
POP H ;Recover message pointer
JMP MSGOUT ;Go back for next character


View File

@@ -0,0 +1,37 @@
; Figure 5-4
;
; Useful subroutines using the Write Console Byte
; BDOS Function.
;
; MSGOUTI (Message Out In-line)
; Output null-byte terminated message that follows the
; CALL to MSGOUTI.
;
; Calling sequence
;
; CALL MSGOUTI
; DB 'Message',0
; ... next instruction
;
; Exit Parameters
; HL -> instruction following message
;
B$CONOUT EQU 2 ;Write console byte
BDOS EQU 5 ;BDOS Entry Point
;
MSGOUTI:
POP H ;HL -> Message
MOV A,M ;Get next data byte
INX H ;Update message pointer
ORA A ;Check if null byte
JNZ MSGOUTIC ;No, continue
PCHL ;Yes, return to next instruction
;after in-line message
MSGOUTIC:
PUSH H ;Save message pointer
MOV E,A ;Ready for BDOS
MVI C,B$CONOUT ;Function code
CALL BDOS
JMP MSGOUTI ;Go back for next char.


View File

@@ -0,0 +1,55 @@
; Figure 5-5
;
; RL$RDR
; Read Line from Reader Device.
; Carriage Returns are ignored, and input terminates
; when specified number of characters have been read
; or a Line Feed is input.
;
; Note : Potential weakness is that there is no
; timeout in this subroutine. It will wait forever
; if no more characters arrive at the Reader device.
;
; Calling sequence
;
; LXI H,BUFFER
; LXI B,MAXCOUNT
; CALL RL$RDR
;
; Exit Parameters
;
; HL -> 00H byte terminating string
; BC = Residual Count (0 if Max. chars read)
; E = Last Character Read
;
B$READIN EQU 3 ;Reader Input
BDOS EQU 5 ;BDOS Entry Point
;
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed (Terminator)
;
RL$RDR:
MOV A,C ;Check if count 0
ORA B ;If count 0 on entry,
MOV E,A ; fake last char. read (00H)
JZ RL$RDRX ;Yes, exit
PUSH B ;Save Max. Chars count
PUSH H ;Save Buffer Pointer
RL$RDRI: ;Loop back here to ignore char.
MVI C,B$READIN
CALL BDOS ;A = Character input
MOV E,A ;Preserve copy of characters
CPI CR ;Check if Carriage Return
JZ RL$RDRI ;Yes, ignore it
POP H ;Recover Buffer pointer
POP B ;Recover Max. Count
CPI LF ;Check if Line Feed
JZ RL$RDRX ;Yes, exit
MOV M,A ;No, store character in buffer
INX H ;Update Buffer pointer
DCX B ;Downdate count
JMP RL$RDR ;Loop back for next char.
RL$RDRX:
MVI M,0 ;Null byte terminate buffer
RET


View File

@@ -0,0 +1,46 @@
; Figure 5-6
;
; WL$PUN
; Write Line to Punch device. Output terminates
; when a 00H byte is encountered.
; A Carriage Return is output when a Line Feed is
; encountered.
;
; Calling sequence
;
; LXI H,BUFFER
; CALL WL$PUN
;
; Exit Parameters
;
; HL -> 00H byte terminator
;
B$PUNOUT EQU 4
BDOS EQU 5
;
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
WL$PUN:
PUSH H ;Save Buffer pointer
MOV A,M ;Get next character
ORA A ;Check if 00H
JZ WL$PUNX ;Yes, exit
CPI LF ;Check if Line Feed
CZ WL$PUNLF ;Yes, O/P CR
MOV E,A ;Character to be output
MVI C,B$PUNOUT ;Function Code
CALL BDOS ;Output character
POP H ;Recover Buffer pointer
INX H ;Update to next char.
JMP WL$PUN ;Output next char
WL$PUNLF: ;Line Feed encountered
MVI C,B$PUNOUT ;Function Code
MVI E,CR ;Output a CR
CALL BDOS
MVI A,LF ;Recreate Line Feed
RET ;Output LF
WL$PUNX: ;Exit
POP H ;Balance the stack
RET


View File

@@ -0,0 +1,46 @@
; Figure 5-7
;
; WL$LST
; Write Line to List device. Output terminates
; when a 00H byte is encountered.
; A Carriage Return is output when a Line Feed is
; encountered.
;
; Calling sequence
;
; LXI H,BUFFER
; CALL WL$LST
;
; Exit Parameters
;
; HL -> 00H byte terminator
;
B$LSTOUT EQU 5
BDOS EQU 5
;
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
WL$LST:
PUSH H ;Save Buffer pointer
MOV A,M ;Get next character
ORA A ;Check if 00H
JZ WL$LSTX ;Yes, exit
CPI LF ;Check if Line Feed
CZ WL$LSTLF ;Yes, O/P CR
MOV E,A ;Character to be output
MVI C,B$LSTOUT ;Function Code
CALL BDOS ;Output character
POP H ;Recover Buffer pointer
INX H ;Update to next char.
JMP WL$LST ;Output next char
WL$LSTLF: ;Line Feed encountered
MVI C,B$LSTOUT ;Function Code
MVI E,CR ;Output a CR
CALL BDOS
MVI A,LF ;Recreate Line Feed
RET ;Output LF
WL$LSTX: ;Exit
POP H ;Balance the stack
RET


View File

@@ -0,0 +1,284 @@
; Figure 5-8
;
;--------------------------------------------------------------
; TESTBED CODE
; Because of the complexity of this subroutine, the actual
; testbed code has been left in this example. It assumes
; that DDT or ZSID will be used to checkout
IF 1 ;Change to IF 0 to disable testbed
ORG 100H
JMP START ;Bypass "variables" setup by DDT
OPTIONS: DB 0 ;Option Flags
TERMS: DB 'A','E','I',0 ;Terminators
BUFFER DB 5 ;Max. Characters in Buffer
DB 0 ;Actual Count
DB 99,99,99,99,99,99,99 ;Data Bytes
START:
LXI H,BUFFER ;Get address of buffer
LXI D,TERMS ;Address of terminator table
LDA OPTIONS ;Get options set by DDT
MOV B,A ;Put in correct register
CALL RCS ;Enter subroutine
CALL 38H ;Force DDT breakpoint
JMP START ;Test again
ENDIF ;End of testbed x`ce
;---------------------------------------------------------------
;
; RCS : Read Console String (using Raw Input)
;
; Reads a string of characters into a memory buffer using
; Raw Input. Supports options :
;
; * to echo characters or not (when echoing, a Carriage
; Return will be echoed followed by Line Feed)
; * warm boot on input of Control-C or not
; * terminating input either on :
; - max. no of chars input
; - matching terminator character
;
; Calling Sequence
;
; LXI H,BUFFER Buffer has structure
; BUFFER: DB 10 Max. size
; DB 0 Actual Read
; DS 10+1 Buffer area
; MVI B,OPTIONS Options required (see EQUates)
; LXI D,TERMS Pointer to 00H-byte terminated
; Chars, any one of which is a terminator.
; CALL RCS
;
; Exit Parameters
;
; BUFFER : Updated with data bytes and actual character count
; input. (Does not include the terminator).
; A = Terminating Code
; 0 = Maximum number of characters input.
; NZ = Terminator character found.
;
RCS$ECHO EQU 0000$0001B ;Input characters to be echoed
RCS$ABORT EQU 0000$0010B ;Abort on Control-C
RCS$FOLD EQU 0000$0100B ;Fold lower case to upper
RCS$TERM EQU 0000$1000B ;DE -> Term. char. set
;
B$DIRCONIO EQU 6 ;Direct console I/O
BDOS EQU 5 ;BDOS entry point
CTL$C EQU 03H ;Control-C
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
BS EQU 08H ;Backspace
RCS$ST: ;Internal standard terminator table
DB 0DH ;Carriage return
DB 0AH ;Line feed
DB 0 ;End of table
;
RCS$BSS: ;Destructive backspace sequence
DB BS,' ',BS,0
;
RCS: ;<<<<< Main Entry
INX H ;HL -> Actual count
MVI M,0 ;Reset to initial state
DCX H ;HL -> Max. count
RCS$L:
PUSH H ;Save buffer pointer
CALL RCS$GC ;Get character and execute :
; ECHO, ABORT, and FOLD options
;C = character input
POP H ;Recover buffer pointer
MVI A,RCS$TERM ;Check if user-specified terminator
ANA B ;B = options
JNZ RCS$UST ;User specified terminators
LXI D,RCS$ST ;Standard terminators
RCS$UST:
CALL RCS$CT ;Check for terminator
JZ RCS$NOTT ;Not terminator
MOV B,A ;Preserve terminating char
RCS$MCI: ;(Max. char input shares this code)
MVI C,0 ;Terminate buffer
CALL RCS$SC ;Save character
MOV A,B ;Recover terminating char
ORA A ;Set flags
RET
RCS$NOTT: ;Not a terminator
MVI A,BS ;Check for backspace
CMP C
JZ RCS$BS ;Backspace entered
CALL RCS$SC ;Save character in buffer
CALL RCS$UC ;Update count
JNZ RCS$L ;Not max. so get another char
MVI B,0 ;Fake terminating char
JMP RCS$MCI ;A = 0 for max. chars input
;
RCS$BS: ;Backspace entered
PUSH H ;Save buffer pointer
INX H ;HL -> actual count
DCR M ;Backup one
JM RCS$NBS ;Check if count now -ve
LXI H,RCS$BSS ;HL -> Backspacing sequence
MVI A,RCS$ECHO ;No, check if echoing
ANA B ;(BS will have been echoed if so)
JZ RCS$BSNE ;No, input BS not echoed
INX H ;Bypass initial Backspace
RCS$BSNE:
PUSH B ;Save Options and character
PUSH D ;Save terminator table pointer
CALL WCS ;Write console string
POP D ;Recover terminator table pointer
POP B ;Recover options and character
JMP RCS$BSX ;Exit from Backspace logic
RCS$NBS:
INR M ;Reset count to 0
RCS$BSX:
POP H ;Recover buffer pointer
JMP RCS$L ;Get next character
RCS$SC: ;Save character in C in buffer
;HL -> buffer pointer
PUSH D ;Save terminator table pointer
PUSH H ;Save buffer pointer
INX H ;HL -> actual count in buffer
MOV E,M ;Get actual count
INR E ;Count of 0 points to first data byte
MVI D,0 ;Make word value of actual count
DAD D ;HL -> next free data byte
MOV M,C ;Save data byte away
POP H ;Recover buffer pointer
POP D ;Recover terminator table pointer
RET
;
RCS$UC: ;Update buffer count and check for max.
;Return Z set if = to max, NZ if not
;HL -> buffer on entry
PUSH H ;Save buffer pointer
MOV A,M ;Get max. count
INX H ;HL -> actual count
INR M ;Increase actual count
CMP M ;Compare max. to actual
POP H ;Recover buffer pointer
RET ;Z-flag set
;
RCS$GC: ;Get character and execute
; ECHO, ABORT and FOLD options
PUSH D ;Save terminator table pointer
PUSH H ;Save buffer pointer
PUSH B ;Save option flags
RCS$WT:
MVI C,B$DIRCONIO ;Function code
MVI E,0FFH ;Specify input
CALL BDOS
ORA A ;Check if data waiting
JZ RCS$WT ;Go back and wait
POP B ;Recover option flags
MOV C,A ;Save data byte
MVI A,RCS$ABORT ;Check if abort option enabled
ANA B
JZ RCS$NA ;No abort
MVI A,CTL$C ;Check for control-C
CMP C
JZ 0 ;Warm boot
RCS$NA:
MVI A,RCS$FOLD ;Check if folding enabled
ANA B
CNZ TOUPPER ;Fold to UPPER CASE
MVI A,RCS$ECHO ;Check if echo required
ANA B
JZ RCS$NE ;No echo required
PUSH B ;Save options and character
MOV E,C ;Move character for output
MVI C,B$DIRCONIO ;Function code
CALL BDOS ;Echo character
POP B ;Recover options and character
MVI A,CR ;Check if Carriage Return
CMP C
JNZ RCS$NE ;No
PUSH B ;Save options and character
MVI C,B$DIRCONIO ;Function code
MVI E,LF ;Output line feed
CALL BDOS
POP B ;Recover options and character
RCS$NE:
POP H ;Recover buffer pointer
POP D ;Recover terminator table
RET ;Character in C
;
RCS$CT: ;Check for terminator
;C = character just input
;DE -> 00-byte character string of Term. Chars
;Returns Z status if no match found, NZ if found
; (with A = C = Terminating character)
PUSH D ;Save table pointer
RCS$CTL:
LDAX D ;Get next terminator character
ORA A ;Check for end of table
JZ RCS$CTX ;No terminator matched
CMP C ;Compare to input character
JZ RCS$CTX ;Terminator matched
INX D ;Move to next terminator
JMP RCS$CTL ;loop to try next character in table
RCS$CTX: ;Check terminator exit
ORA A ;At this point, A will either be 0
; if the end of the table has been
; reached, or NZ if a match has been
; found. The Z-flag will be set.
POP D ;Recover table pointer
RET
;
; TOUPPER - Fold lower case letters to upper
;
; C = Character on entry and exit
;
TOUPPER:
MVI A,'a'-1 ;Check if folding needed
CMP C ;Compare to input char
JNC TOUPX ;No, char is < or = 'a'-1
MVI A,'z' ;Maybe, char is = or > 'a'
CMP C
JC TOUPX ;No, char is > 'z'
MVI A,0DFH ;Fold character
ANA C
MOV C,A ;Return folded character
TOUPX:
RET
;
;
; WCS - Write Console String (Using Raw I/O)
;
; Output terminates when a 00H byte is encountered.
; A Carriage Return is output when a Line Feed is
; encountered.
;
; Calling sequence
;
; LXI H,BUFFER
; CALL WCS
;
; Exit Parameters
;
; HL -> 00H byte terminator
;
;
WCS:
PUSH H ;Save buffer pointer
MOV A,M ;Get next character
ORA A ;Check if 00H
JZ WCSX ;Yes, exit
CPI LF ;Check if Line Feed
CZ WCSLF ;Yes, O/P CR
MOV E,A ;Character to be output
MVI C,B$DIRCONIO ;Function Code
CALL BDOS ;Output character
POP H ;Recover Buffer pointer
INX H ;Update to next char.
JMP WCS ;Output next char
WCSLF: ;Line Feed encountered
MVI C,B$DIRCONIO ;Function Code
MVI E,CR ;Output a CR
CALL BDOS
MVI A,LF ;Recreate Line Feed
RET ;Output LF
WCSX: ;Exit
POP H ;Balance the stack
RET


View File

@@ -0,0 +1,252 @@
; Figure 7-5.
;
; Example Put CP/M
;
; This program writes out the CP/M Cold Boot Loader, the
; CCP, BDOS and BIOS to a floppy diskette. It runs under
; CP/M as a normal Transient Program.
;
Version EQU '01' ;Equates used in the sign on message
Month EQU '07'
Day EQU '24'
Year EQU '82'
;
;
; The actual PUTCPMF5.COM program consists of this code,
; plus the BOOTF5.HEX, the CCP, BDOS and BIOS.
;
; When this program executes, the memory image should
; look like this :
;
; Component Base Address
; BIOS 1F80H
; BDOS 1180H
; CCP 0980H
; BOOTF5 0780H
;
; The components are produced as follows :
;
; BIOS.HEX By assembling source code
; BDOS ) From a CPMnn.COM file output
; CCP ) by MOVCPM and SAVEd on disk
; BOOTF5.HEX By assembling source code
;
; The components are pieced together using DDT with the
; following commands :
;
; DDT CPMnn.COM
; IPUTCPMF5.HEX
; R (Reads in this program)
; IBOOTF5.HEX
; R680 (Reads in Boot at 0780H)
; IBIOS.HEX
; R2980 (Reads in BIOS at 1F80H)
; G0 (Exit from DDT)
; SAVE 40 PUTCPMF5.COM (Create final .COM file)
;
; The actual layout of the diskette is as follows :
;
; Track 0 Sector
; 1 2 3 4 5 6 7 8 9
; Head +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 0 |Boot |<======== CCP ========>|<======= BDOS ========|
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 1 |====== BDOS ====>|<============= BIOS ============>|
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 10 11 12 13 14 15 16 17 18
; Sector
;
; Equates for defining memory size and the base address and
; length of the system components.
;
Memory$Size EQU 64 ;Number of Kbytes of RAM
;
; The BIOS Length must match that declared in the BIOS.
;
BIOS$Length EQU 0900H
;
Boot$Length EQU 512
CCP$Length EQU 0800H ;Constant
BDOS$Length EQU 0E00H ;Constant
;
Length$In$Bytes EQU CCP$Length + BDOS$Length + BIOS$Length
;
Start$Image EQU 980H - Boot$Length ;Address of CP/M Image
Length$Image EQU Length$In$Bytes + Boot$Length
;
;
; Disk Characteristics
;
; These equates describe the physical characteristics of
; the floppy diskette so that the program can move from
; one sector to the next, updating the track and resetting
; the sector when necessary.
;
First$Sector$on$Track EQU 1
Last$Sector$on$Track EQU 18
Last$Sector$on$Head$0 EQU 9
Sector$Size EQU 512
;
;
; Controller Characteristics
;
; On this computer system, the floppy disk controller can write
; multiple sectors in a single command. However, in order to
; to produce a more general example it is shown only reading one
; sector at a time.
;
Sectors$Per$Write EQU 1
;
;
; Cold Boot Characteristics
;
Start$Track EQU 0 ;Initial values for CP/M image
Start$Sector EQU 1 ;= " =
Sectors$To$Write EQU (Length$Image + Sector$Size - 1) / Sector$Size
;
;
B$PRINTS EQU 9 ;Print string terminated by $
BDOS EQU 5 ;BDOS Entry Point
;
;
ORG 100H
Put$CPM:
JMP Main$Code ;Enter main code body
;For reasons of clarity, the main
; data structures are shown before the
; executable code.
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
Signon$Message:
DB CR,LF,'Put CP/M on Diskette'
DB CR,LF
DB 'Version '
DW Version
DB ' '
DW Month
DB '/'
DW Day
DB '/'
DW Year
DB CR,LF,'$'
;
; Disk Control Tables
;
Disk$Control$5 EQU 45H ;5 1/4" Control Byte
Command$Block$5 EQU 46H ;Control Table Pointer
Disk$Status EQU 43H ;Completion status
;
;
; The command table Track and DMA$Address can also be used
; as working storage and updated as the load process
; continues. The Sector in the command table cannot be
; used directly as the disk controller requires it to be
; the sector number on the specified head (1 - 9) rather
; than the sector number on track. Hence a separate variable
; must be used.
;
Sector: DB Start$Sector
;
Command$Table: DB 02H ;Command - Write
Unit: DB 0 ;Unit (Drive) number = 0 or 1
Head: DB 0 ;Head number = 0 or 1
Track: DB Start$Track ;Used as working variable
Sector$on$head: DB 0 ;Converted by low-level driver
Byte$Count: DW Sector$Size * Sectors$Per$Write
DMA$Address: DW Start$Image
Next$Status: DW Disk$Status ;Pointer to next Status Block
; if commands are chained.
Next$Control: DW Disk$Control$5 ;Pointer to next Control Byte
; if commands are chained.
Main$Code:
LXI SP,Put$CPM ;Stack grows down below code
LXI D,Signon$Message ;Sign on
MVI C,B$PRINTS ;Print string until $
CALL BDOS
LXI H,Command$Table ;Point the disk controller at
SHLD Command$Block$5 ; the command block
MVI C,Sectors$To$Write ;Set sector count
Write$Loop:
CALL Put$CPM$Write ;Write data onto diskette
DCR C ;Downdate sector count
JZ 0 ;Warm Boot
LXI H,Sector ;Update sector number
MVI A,Sectors$Per$Write ; by adding on number of sectors
ADD M ; by controller
MOV M,A ;Save result
MVI A,Last$Sector$On$Track + 1 ;Check if at end of track
CMP M
JNZ Not$End$Track
MVI M,First$Sector$On$Track ;Yes, reset to beginning
LHLD Track ;Update Track number
INX H
SHLD Track
Not$End$Track:
LHLD DMA$Address ;Update DMA Address
LXI D,Sector$Size * Sectors$Per$Write
DAD D
SHLD DMA$Address
JMP Write$Loop ;Write next block
;
Put$CPM$Write: ;At this point, the description of the
; operation required is in the variables
; contained in the Command Table, along
; with the Sector variable.
PUSH B ;Save sector count in C
;------ Change this routine to match the disk controller in use ------
MVI B,0 ;Assume head 0
LDA Sector ;Get requested sector
MOV C,A ;Take a copy of it
CPI Last$Sector$on$Head$0+1 ;Check if on head 1
JC Head$0 ;No
SUI Last$Sector$on$Head$0 ;Bias down for head 1
MOV C,A ;Save copy
INR B ;Set head 1
Head$0:
MOV A,B ;Get head
STA Head
MOV A,C ;Get sector
STA Sector$On$Head
LXI H,Disk$Control$5 ;Activate controller
MVI M,80H
Wait$For$Boot$Complete:
MOV A,M ;Get status byte
ORA A ;Check if complete
JNZ Wait$For$Boot$Complete ;No
;Yes, check for errors
LDA Disk$Status
CPI 80H
JC Put$CPM$Error ;Yes, an error occurred
;------ End of Physical Write routine ------
POP B ;Recover sector count in C
RET
;
Put$CPM$Error:
LXI D,Put$CPM$Error$Message
MVI C,B$PRINTS ;Print string until $
CALL BDOS ;Output error message
JMP Main$Code ;Restart the loader
;
Put$CPM$Error$Message:
DB CR,LF,'Error in writing CP/M - retrying...',CR,LF,'$'
END Put$CPM


View File

@@ -0,0 +1,272 @@
; Figure 7-7.
;
; Example CP/M Cold Bootstrap Loader
;
; This program is written out to Track 0, Head 0, Sector 1
; by the PUTCPMF5 program.
; It is loaded into memory at location 100H on up by the
; PROM-based bootstrap mechanism that gets control of the
; CPU on power-up or system reset.
;
Version EQU '01' ;Equates used in the sign on message
Month EQU '07'
Day EQU '24'
Year EQU '82'
;
Debug EQU 0 ;Set Non-zero to debug as normal
; transient program
;
; The actual layout of the diskette is as follows :
;
; Track 0 Sector
; 1 2 3 4 5 6 7 8 9
; Head +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 0 |Boot |<======== CCP ========>|<======= BDOS ========|
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 1 |====== BDOS ====>|<============= BIOS ============>|
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 10 11 12 13 14 15 16 17 18
; Sector
;
; Equates for defining memory size and the base address and
; length of the system components.
;
Memory$Size EQU 64 ;Number of Kbytes of RAM
;
; The BIOS Length must match that declared in the BIOS.
;
BIOS$Length EQU 0900H
;
CCP$Length EQU 0800H ;Constant
BDOS$Length EQU 0E00H ;Constant
;
Length$In$K EQU ((CCP$Length + BDOS$Length + BIOS$Length) / 1024) + 1
Length$In$Bytes EQU CCP$Length + BDOS$Length + BIOS$Length
;
IF NOT Debug
CCP$Entry EQU (Memory$Size - Length$In$K) * 1024
ENDIF
IF Debug
CCP$Entry EQU 3980H ;Read into a lower address.
;This address is chosen to be above
; the area into which DDT initially loads
; and the 980H makes the addresses similar
; to the SYSGEN values so that the memory
; image can be checked with DDT.
ENDIF
BDOS$Entry EQU CCP$Entry + CCP$Length + 6
BIOS$Entry EQU CCP$Entry + CCP$Length + BDOS$Length
;
;
; Disk Characteristics
;
; These equates describe the physical characteristics of
; the floppy diskette so that the program can move from
; one sector to the next, updating the track and resetting
; the sector when necessary.
;
First$Sector$on$Track EQU 1
Last$Sector$on$Track EQU 18
Last$Sector$on$Head$0 EQU 9
Sector$Size EQU 512
;
;
; Controller Characteristics
;
; On this computer system, the floppy disk controller can read
; multiple sectors in a single command. However, in order to
; to produce a more general example it is shown only reading one
; sector at a time.
;
Sectors$Per$Read EQU 1
;
;
; Cold Boot Characteristics
;
Start$Track EQU 0 ;Initial values for CP/M image
Start$Sector EQU 2 ;= " =
Sectors$To$Read EQU (Length$In$Bytes + Sector$Size - 1) / Sector$Size
;
;
;
ORG 100H
Cold$Boot$Loader:
JMP Main$Code ;Enter main code body
;For reasons of clarity, the main
; data structures are shown before the
; executable code.
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
Signon$Message:
DB CR,LF,'CP/M Bootstrap Loader'
IF Debug
DB ' (Debug)'
ENDIF
DB CR,LF
DB 'Version '
DW Version
DB ' '
DW Month
DB '/'
DW Day
DB '/'
DW Year
DB CR,LF,0
;
; Disk Control Tables
;
Disk$Control$5 EQU 45H ;5 1/4" Control Byte
Command$Block$5 EQU 46H ;Control Table Pointer
Disk$Status EQU 43H ;Completion status
;
;
; The command table Track and DMA$Address can also be used
; as working storage and updated as the load process
; continues. The Sector in the command table cannot be
; used directly as the disk controller requires it to be
; the sector number on the specified head (1 - 9) rather
; than the sector number on track. Hence a separate variable
; must be used.
;
Sector: DB Start$Sector
;
Command$Table: DB 01H ;Command - Read
Unit: DB 0 ;Unit (Drive) number = 0 or 1
Head: DB 0 ;Head number = 0 or 1
Track: DB Start$Track ;Used as working variable
Sector$on$head: DB 0 ;Converted by low-level driver
Byte$Count: DW Sector$Size * Sectors$Per$Read
DMA$Address: DW CCP$Entry
Next$Status: DW Disk$Status ;Pointer to next Status Block
; if commands are chained.
Next$Control: DW Disk$Control$5 ;Pointer to next Control Byte
; if commands are chained.
Main$Code:
LXI SP,Cold$Boot$Loader ;Stack grows down below code
LXI H,Signon$Message ;Sign on
CALL Display$Message
LXI H,Command$Table ;Point the disk controller at
SHLD Command$Block$5 ; the command block
MVI C,Sectors$To$Read ;Set sector count
Load$Loop:
CALL Cold$Boot$Read ;Read data into memory
DCR C ;Downdate sector count
IF NOT Debug
JZ BIOS$Entry ;Enter BIOS when load done
ENDIF
IF Debug
JZ 0 ;Warm Boot
ENDIF
LXI H,Sector ;Update sector number
MVI A,Sectors$Per$Read ; by adding on number of sectors
ADD M ; by controller
MOV M,A ;Save result
MVI A,Last$Sector$On$Track + 1 ;Check if at end of track
CMP M
JNZ Not$End$Track
MVI M,First$Sector$On$Track ;Yes, reset to beginning
LHLD Track ;Update Track number
INX H
SHLD Track
Not$End$Track:
LHLD DMA$Address ;Update DMA Address
LXI D,Sector$Size * Sectors$Per$Read
DAD D
SHLD DMA$Address
JMP Load$Loop ;Read next block
;
Cold$Boot$Read: ;At this point, the description of the
; operation required is in the variables
; contained in the Command Table, along
; with the Sector variable.
PUSH B ;Save sector count in C
;------ Change this routine to match the disk controller in use ------
MVI B,0 ;Assume head 0
LDA Sector ;Get requested sector
MOV C,A ;Take a copy of it
CPI Last$Sector$on$Head$0+1 ;Check if on head 1
JC Head$0 ;No
SUI Last$Sector$on$Head$0 ;Bias down for head 1
MOV C,A ;Save copy
INR B ;Set head 1
Head$0:
MOV A,B ;Get head
STA Head
MOV A,C ;Get sector
STA Sector$On$Head
LXI H,Disk$Control$5 ;Activate controller
MVI M,80H
Wait$For$Boot$Complete:
MOV A,M ;Get status byte
ORA A ;Check if complete
JNZ Wait$For$Boot$Complete ;No
;Yes, check for errors
LDA Disk$Status
CPI 80H
JC Cold$Boot$Error ;Yes, an error occurred
;------ End of Physical Read routine ------
POP B ;Recover sector count in C
RET
;
Cold$Boot$Error:
LXI H,Cold$Boot$Error$Message
CALL Display$Message ;Output error message
JMP Main$Code ;Restart the loader
;
Cold$Boot$Error$Message:
DB CR,LF,'Bootstrap Loader Error - retrying...',CR,LF,0
;
; Equates for Terminal Output
;
Terminal$Status$Port EQU 01H
Terminal$Data$Port EQU 02H
;
Terminal$Output$Ready EQU 0000$0001B
;
;
Display$Message: ;Displays the specified message on the console.
;On entry, HL points to a stream of bytes to be
;output. A 00H-byte terminates the message.
MOV A,M ;Get next message byte
ORA A ;Check if terminator
RZ ;Yes, return to caller
MOV C,A ;Prepare for output
Output$Not$Ready:
IN Terminal$Status$Port ;Check if ready for output
ANI Terminal$Output$Ready
JZ Output$Not$Ready ;No, wait
MOV A,C ;Get Data character
OUT Terminal$Data$Port ;Output to Screen
INX H ;Move to next byte of message
JMP Display$Message ;Loop until complete message output
;The PROM-based bootstrap loader checks
; to see that the characters "CP/M"
; are on the diskette bootstrap sector
; before it transfers control to it.
ORG 2E0H
DB 'CP/M'
END Cold$Boot$Loader


File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
; Figure 8-6
;
; Device Table Equates
; The drivers use an Device Table for each
; Physical Device they service. The EQUates that follow
; are used to access the various fields within the
; Device Table.
;
; Port Numbers and Status Bits
DT$Status$Port EQU 0 ;Device Status Port number
DT$Data$Port EQU DT$Status$Port+1
;Device Data Port number
DT$Output$Ready EQU DT$DataPort+1
;Output ready status mask
DT$Input$Ready EQU DT$Output$Ready+1
;Input ready status mask
DT$DTR$Ready EQU DT$Input$Ready+1
;DTR ready to send mask
DT$Reset$Int$Port EQU DT$DTR$Ready+1
;Port number used to reset an
; interrupt
DT$Reset$Int$Value EQU DT$Reset$Int$Port+1
;Value output to reset interrupt
DT$Detect$Error$Port EQU DT$Reset$Int$Value+1
;Port number for error detect
DT$Detect$Error$Value EQU DT$Detect$Error$Port+1
;Mask for detecting error (parity etc.)
DT$Reset$Error$Port EQU DT$Detect$Error$Value+1
;Output to port to reset error
DT$Reset$Error$Value EQU DT$Reset$Error$Port+1
;Value to output to reset error
DT$RTS$Control$Port EQU DT$Reset$Error$Value+1
;Control port for lowering RTS
DT$Drop$RTS$Value EQU DT$RTS$Control$Port+1
;Value, when output, to drop RTS
DT$Raise$RTS$Value EQU DT$Drop$RTS$Value+1
;Value, when output, to raise RTS
;
; Device Logical Status (incl. Protocols)
DT$Status EQU DT$Raise$RTS$Value+1
;Status Bits
DT$Output$Suspend EQU 0000$0001B ;Output suspended pending
; Protocol action
DT$Input$Suspend EQU 0000$0010B ;Input suspended until
; buffer empties
DT$Output$DTR EQU 0000$0100B ;Output uses DTR high to send
DT$Output$Xon EQU 0000$1000B ;Output uses Xon/Xoff
DT$Output$Etx EQU 0001$0000B ;Output uses Etx/Ack
DT$Output$Timeout EQU 0010$0000B ;Output uses Timeout
DT$Input$RTS EQU 0100$0000B ;Input uses RTS high to receive
DT$Input$Xon EQU 1000$0000B ;Input uses Xon/Xoff
;
DT$Status$2 EQU DT$Status+1 ;Secondary Status Byte
DT$Fake$Typeahead EQU 0000$0001B ;Requests Input$Status to
;return "Data Ready" when
;Control Characters are in
;input buffer
;
DT$Etx$Count EQU DT$Status$2+1
;No. of chars sent in Etx protocol
DT$Etx$Message$Length EQU DT$Etx$Count+2
;Specified message length
;
; Input Buffer values
DT$Buffer$Base EQU DT$Etx$Message$Length+2
;Address of Input Buffer
DT$Put$Offset EQU DT$Buffer$Base+2
;Offset for Putting chars into buffer
DT$Get$Offset EQU DT$Put$Offset+1
;Offset for Getting chars from buffer
DT$Buffer$Length$Mask EQU DT$Get$Offset+1
;Length of buffer - 1
;Note : Buffer length must always be
; a binary number; e.g. 32, 64 or 128
;This mask then becomes :
; 32 -> 31 (0001$1111B)
; 64 -> 63 (0011$1111B)
; 128 -> 127 (0111$1111B)
;After the Get/Put offset has been
;incremented it is ANDed with the mask
;to reset it to zero when the end of
;the buffer has been reached.
DT$Character$Count EQU DT$Buffer$Length$Mask+1
;Count of the number of characters
; currently in the buffer
DT$Stop$Input$Count EQU DT$Character$Count+1
;Stop input when the count reaches
; this value
DT$Resume$Input$Count EQU DT$Stop$Input$Count+1
;Resume input when the count reaches
; this value
DT$Control$Count EQU DT$Resume$Input$Count+1
;Count of the number of control
; characters in the buffer
DT$Function$Delay EQU DT$Control$Count+1
;Number of clock ticks to delay to
; allow all characters after function
; key lead-in to arrive
DT$Initialize$Stream EQU DT$Function$Delay+1
;Address of byte stream necessary to
; initialize this device


View File

@@ -0,0 +1,342 @@
; Figure 9-5
;
; This shows slightly more user-friendly error processor
; for disk errors than that shown in the enhanced BIOS
; in Figure 8-10.
; This version outputs a recommended course of action
; depending on the nature of the error detected.
; Code that remains unchanged from Figure 8-10 has been
; abbreviated.
;
; Dummy Equates and data declarations needed to get
; an error free assembly of this example.
;
Floppy$Read$Code EQU 01H ;Read Command for controller
Floppy$Write$Code EQU 02H ;Write Command for controller
;
Disk$Hung$Flag: DB 0 ;Set NZ when Watchdog timer times
; out.
Disk$Timer EQU 600 ;10 seconds delay (16.66ms tick)
;
Disk$Status$Block EQU 43H ;Address in memory where controller
; returns status
;Values from controller command table
Floppy$Command: DB 0
Floppy$Head: DB 0
Floppy$Track: DB 0
Floppy$Sector: DB 0
Deblocking$Required: DB 0 ;Flag set by SELDSK according
; to selected disk type
Disk$Error$Flag: DB 0 ;Error flag returned to BDOS
;
In$Buffer$Disk: DB 0 ;Logical disk Id. relating to current
; disk sector in deblocking buffer
;
; Equates for Messages
;
BELL EQU 07H ;Sound terminal Bell
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
BDOS EQU 5 ;BDOS Entry Point (for system reset)
;
;
;
;
No$Deblock$Retry:
;----------------------------------------------------
; Omitted code to setup disk controller command table
; and initiate the disk operation
;----------------------------------------------------
JMP Wait$For$Disk$Complete
;
;
Write$Physical: ;Write contents of Disk Buffer to
; correct sector.
MVI A,Floppy$Write$Code ;Get Write Function code
JMP Common$Physical ;Go to common code
Read$Physical: ;Read previously selected Sector
; into Disk Buffer.
MVI A,Floppy$Read$Code ;Get Read Function code
Common$Physical:
STA Floppy$Command ;Set command table
;
Deblock$Retry: ;Re-entry point to re-try after error
;---------------------------------------------------
; Omitted code sets up disk controller command block
; and initiates the disk operation
;---------------------------------------------------
;
Wait$For$Disk$Complete: ;Wait until Disk Status Block indicates
; operation has completed, then check
; if any errors occurred.
;On entry HL -> Disk Control byte
XRA A ;Ensure hung flag clear
STA Disk$Hung$Flag
LXI H,Disk$Timed$Out ;Setup Watchdog timer
LXI B,Disk$Timer ;Time delay
CALL Set$Watchdog
Disk$Wait$Loop:
MOV A,M ;Get control byte
ORA A
JZ Disk$Complete ;Operation done
LDA Disk$Hung$Flag ;Also check if timed out
ORA A
JNZ Disk$Error ;Will be set to 40H
JMP Disk$Wait$Loop
Disk$Timed$Out: ;Control arrives here from Watchdog
; routine itself - so this is effectively
; part of the interrupt service routine.
MVI A,40H ;Set Disk Hung error code
STA Disk$Hung$Flag ; into error flag to pull
; control out of loop
RET ;Return to Watchdog routine
Disk$Complete:
LXI B,0 ;Reset Watchdog timer
;HL is irrelevant here
CALL Set$Watchdog
LDA Disk$Status$Block ;Complete - now check status
CPI 80H ;Check if any errors occurred
JC Disk$Error ;Yes
;
Disk$Error$Ignore:
XRA A ;No
STA Disk$Error$Flag ;Clear error flag
RET
;
; Disk Error Message handling
;
;
Disk$Error$Messages: ;This table is scanned, comparing the
; Disk Error Status with those in the
; table. Given a match, or even when
; then end of the table is reached, the
; address following the status value
; points to the correct advisory message text.
; Following this is the address of an
; error description message.
DB 40H
DW Disk$Advice1,Disk$Msg$40
DB 41H
DW Disk$Advice2,Disk$Msg$41
DB 42H
DW Disk$Advice3,Disk$Msg$42
DB 21H
DW Disk$Advice4,Disk$Msg$21
DB 22H
DW Disk$Advice5,Disk$Msg$22
DB 23H
DW Disk$Advice5,Disk$Msg$23
DB 24H
DW Disk$Advice6,Disk$Msg$24
DB 25H
DW Disk$Advice6,Disk$Msg$25
DB 11H
DW Disk$Advice7,Disk$Msg$11
DB 12H
DW Disk$Advice7,Disk$Msg$12
DB 13H
DW Disk$Advice7,Disk$Msg$13
DB 14H
DW Disk$Advice7,Disk$Msg$14
DB 15H
DW Disk$Advice7,Disk$Msg$15
DB 16H
DW Disk$Advice7,Disk$Msg$16
DB 0 ;<== Terminator
DW Disk$Advice7,Disk$Msg$Unknown ;Unmatched code
;
DEM$Entry$Size EQU 5 ;Entry size in Error Message Table
;
;
; Message Texts
;
Disk$Msg$40: DB 'Hung',0 ;Timeout message
Disk$Msg$41: DB 'Not Ready',0
Disk$Msg$42: DB 'Write Protected',0
Disk$Msg$21: DB 'Data',0
Disk$Msg$22: DB 'Format',0
Disk$Msg$23: DB 'Missing Data Mark',0
Disk$Msg$24: DB 'Bus Timeout',0
Disk$Msg$25: DB 'Controller Timeout',0
Disk$Msg$11: DB 'Drive Address',0
Disk$Msg$12: DB 'Head Address',0
Disk$Msg$13: DB 'Track Address',0
Disk$Msg$14: DB 'Sector Address',0
Disk$Msg$15: DB 'Bus Address',0
Disk$Msg$16: DB 'Illegal Command',0
Disk$Msg$Unknown: DB 'Unknown',0
;
Disk$EM$1: ;Main disk error message - part 1
DB BELL,CR,LF
DB 'Disk ',0
;
;Error Text output next
;
Disk$EM$2: ;Main disk error message - part 2
DB ' Error ('
Disk$EM$Status: DB 0,0 ;Status code in Hex
DB ')',CR,LF,' Drive '
Disk$EM$Drive: DB 0 ;Disk Drive code, A,B...
DB ', Head '
Disk$EM$Head: DB 0 ;Head number
DB ', Track '
Disk$EM$Track: DB 0,0 ;Track number
DB ', Sector '
Disk$EM$Sector: DB 0,0 ;Sector number
DB ', Operation - '
DB 0 ;Terminator
;
Disk$EM$Read: DB 'Read.',0 ;Operation names
Disk$EM$Write: DB 'Write.',0
;
Disk$Advice0: DB CR,LF,' ',0
Disk$Advice1: DB 'Check disk loaded, Retry',0
Disk$Advice2: DB 'Possible hardware problem',0
Disk$Advice3: DB 'Write enable if correct disk, Retry',0
Disk$Advice4: DB 'Retry several times',0
Disk$Advice5: DB 'Reformat disk or use another disk',0
Disk$Advice6: DB 'Hardware error, Retry',0
Disk$Advice7: DB 'Hardware or Software error, Retry',0
;
Disk$Advice9: DB ', or call for help if error persists',CR,LF
;
Disk$Action$Confirm:
DB 0 ;Set to character entered by user
DB CR,LF,0
;
; Disk Error Processor
;
; This routine builds and outputs an error message.
; The user is then given the opportunity to :
;
; R - Retry the operation that caused the error.
; I - Ignore the error and attempt to continue.
; A - Abort the program and return to CP/M.
;
Disk$Error:
PUSH PSW ;Preserve error code from controller
LXI H,Disk$EM$Status ;Convert code for message
CALL CAH ;Converts A to hex
LDA In$Buffer$Disk ;Convert disk id. for message
ADI 'A' ;Make into letter
STA Disk$EM$Drive
LDA Floppy$Head ;Convert head number
ADI '0'
STA Disk$EM$Head
LDA Floppy$Track ;Convert track number
LXI H,Disk$EM$Track
CALL CAH
LDA Floppy$Sector ;Convert sector number
LXI H,Disk$EM$Sector
CALL CAH
LXI H,Disk$EM$1 ;Output first part of message
CALL Output$Error$Message
POP PSW ;Recover error status code
MOV B,A ;For comparisons
LXI H,Disk$Error$Messages - DEM$Entry$Size
;HL -> Table - one entry
LXI D,DEM$Entry$Size ;For loop below
Disk$Error$Next$Code:
DAD D ;Move to next (or first) entry
MOV A,M ;Get code number from table
ORA A ;Check if end of table
JZ Disk$Error$Matched ;Yes - pretend a match occurred
CMP B ;Compare to actual code
JZ Disk$Error$Matched ;Yes - exit from loop
JMP Disk$Error$Next$Code ;Check next code
;
Disk$Error$Matched:
INX H ;HL -> Advisory text address
MOV E,M
INX H
MOV D,M ;DE -> Advisory test
PUSH D ;Save for later
INX H ;HL -> Message text address
MOV E,M ;Get Address into DE
INX H
MOV D,M
XCHG ;HL -> Text
CALL Output$Error$Message ;Display explanatory text
LXI H,Disk$EM$2 ;Display second part of message
CALL Output$Error$Message
LXI H,Disk$EM$Read ;Choose operation text
; (assume a read)
LDA Floppy$Command ;Get controller command
CPI Floppy$Read$Code
JZ Disk$Error$Read ;Yes
LXI H,Disk$EM$Write ;No - change address in HL
Disk$Error$Read:
CALL Output$Error$Message ;Display operation type
LXI H,Disk$Advice0 ;Display leading blanks
CALL Output$Error$Message
POP H ;Recover Advisory text pointer
CALL Output$Error$Message
LXI H,Disk$Advice9 ;Display trailing component
CALL Output$Error$Message
;
Disk$Error$Request$Action: ;Ask the user what to do next
CALL Request$User$Choice ;Display prompt and get single
; character response (folded to
; upper case)
CPI 'R' ;Retry?
JZ Disk$Error$Retry
CPI 'A' ;Abort
JZ System$Reset
CPI 'I' ;Ignore
JZ Disk$Error$Ignore
JMP Disk$Error$Request$Action
;
Disk$Error$Retry: ;The decision on where to return to
; depends on whether the operation
; failed on a deblocked or
; non-deblocked drive
LDA Deblocking$Required
ORA A
JNZ Deblock$Retry
JMP No$Deblock$Retry
;
System$Reset: ;This is a radical approach, but
; it does cause CP/M to restart
MVI C,0 ;System Reset
CALL BDOS
;
; Omitted subroutines (listed in full in Figure 8-10)
;
Set$Watchdog: ;Set Watchdog timer (to number of "ticks" in BC, and
; to transfer control to (HL) if timer hits zero).
CAH: ;Convert A to two ASCII hex characters, storing
; the output in (HL) and (HL+1)
Output$Error$Message: ;Display the 00-byte terminated error message
; pointed to by HL. Output is directed only to
; those console devices not being used for list
; output as well.
Request$User$Choice: ;Display prompt "Enter R, A, I..." and return
; single keyboard character (upper case) in A
RET ;Dummy


View File

@@ -0,0 +1,167 @@
#define VN "1.0 02/11/83"
/* FIND - This utility displays either a map showing on which disks and
in which user numbers files matching the specified ambiguous
file name are found, or it can display the actual names matched. */
#include <LIBRARY.H>
struct _dirpb dir_pb; /* directory management parameter block */
struct _dir *dir_entry; /* pointer to directory entry (somewhere in
dir_pb) */
struct _scb scb; /* search control block */
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
short cur_disk; /* current logical disk at start of program */
int mcount; /* match count (no. of file names matched) */
int dmcount; /* per disk match count */
int lcount; /* line count (for lines displayed) */
int map_flag; /* 0 = show file names of matched files,
NZ = show map of number of files */
/* The array below is used to tabulate the results for each
disk drive, and for each user number on the drive.
In addition, two extra "users" have been added for "Free"
and "Used" values. */
unsigned disk_map[16][18]; /* Disk A -> P, Users 0 -> 15, Free, Used */
#define USED_COUNT 16 /* "user" number for Used entities */
#define FREE_COUNT 17 /* "user" number for Free entities */
main(argc,argv)
short argc; /* argument count */
char *argv[]; /* argument vector (pointer to an array of chars) */
{
printf("\nFIND Version %s (Library %s)",VN,LIBVN);
chk_use(argc); /* check usage */
cur_disk = bdos(GETDISK); /* get current default disk */
dm_clr(disk_map); /* reset disk map */
/* set search control block
disks, name, type, user number, extent number,
and number of bytes to compare - in this case, match all users,
but only extent 0 */
setscb(scb,argv[1],'?',0,13); /* set disks, name, type */
map_flag = usstrcmp("NAMES",argv[2]); /* set flag for map option */
lcount = dmcount = mcount = 0; /* initialize counts */
for (scb.scb_disk = 0; /* starting with logical disk A: */
scb.scb_disk < 16; /* until logical disk P: */
scb.scb_disk++) /* move to next logical disk */
{
/* check if current disk has been selected for search */
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
continue; /* no - so bypass this disk */
printf("\nSearching disk : %c",(scb.scb_disk + 'A'));
lcount++; /* update line count */
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
dmcount = 0; /* reset disk matched count */
if (!map_flag) /* if file names are to be displayed */
putchar('\n'); /* move to column 1 */
/* set the directory to "closed", and force the get_nde
function to open it. */
dir_pb.dp_open = 0;
/* while not at the end of the directory, set a pointer to the
next directory entry. */
while(dir_entry = get_nde(dir_pb))
{
/* check if entry in use in order to update
the free/used counts */
if (dir_entry -> de_userno == 0xE5) /* unused */
disk_map[scb.scb_disk][FREE_COUNT]++;
else /* in use */
disk_map[scb.scb_disk][USED_COUNT]++;
/* select only those active entries that are the
first extent (numbered 0) of a file that matches
the name supplied by the user. */
if (
(dir_entry -> de_userno != 0xE5) &&
(dir_entry -> de_extent == 0) &&
(comp_fname(scb,dir_entry) == NAME_EQ)
)
{
mcount++; /* update matched counts */
dmcount++; /* per disk count */
if (map_flag) /* check map option */
{
/* update disk map */
disk_map[scb.scb_disk][dir_entry -> de_userno]++;
}
else /* display names */
{
conv_dfname(scb.scb_disk,dir_entry,file_name);
printf("%s ",file_name);
/* check if need to start new line */
if (!(dmcount % 4))
{
putchar('\n');
if (++lcount > 18)
{
lcount = 0;
printf("\nPress Space Bar to continue....");
getchar();
putchar('\n');
}
}
}
}
} /* end of directory */
} /* all disks searched */
if (map_flag)
{
printf("\n Numbers show files in each User Number.");
printf("\n --- User Numbers --- Dir. Entries");
dm_disp(disk_map,scb.scb_adisks); /* display disk map */
}
if (mcount == 0)
printf("\n --- File Not Found --- ");
bdos(SETDISK,cur_disk); /* reset to current disk */
}
chk_use(argc) /* check usage */
/* This function checks that the correct number of
parameters has been specified, outputting instructions
if not.
*/
/* Entry Parameter */
int argc; /* Count of the number of arguments on the command line */
{
/* The minimum value of argc is 1 (for the program name itself),
so argc is always one greater than the number of parameters
on the command line */
if (argc == 1 || argc > 3)
{
printf("\nUsage :");
printf("\n\tFIND d:filename.typ {NAMES}");
printf("\n\t *:filename.typ (All disks)");
printf("\n\t ABCD..OP:filename.typ (Selected Disks)");
printf("\n\tNAMES option shows actual names rather than map.");
exit();
}
}


View File

@@ -0,0 +1,149 @@
#define VN "\nFUNKEY Vn 1.0 02/18/83"
#include <LIBRARY.H>
int fnum; /* function key number to be programmed */
char fstring[20]; /* string for function key */
struct _fkt *pfk; /* pointer to function key table */
main(argc,argv)
int argc;
char *argv[];
{
if (argc == 1 || argc > 3)
show_use();
pfk = get_cba(CB_FKT); /* set pointer to function key table */
if (usstrcmp("SHOW",argv[1]))
{
if (!isdigit(argv[1][0]))
{
printf("\n\007'%s' is an illegal function key.",
argv[1]);
show_use();
}
fnum = atoi(argv[1]); /* convert function key number */
if (fnum > FK_ENTRIES)
{
printf("\n\007Function key number %d too large.",fnum);
show_use();
}
if (get_fs(fstring) > FK_LENGTH)
{
printf("\n\007Function key string is too long.");
show_use();
}
pfk += fnum; /* update pointer to string */
/* copy string into function key table */
/* check if function key input present */
if (!(pfk -> fk_input[0]))
{
printf("\n\007Error : Function Key #%d is not set up to be programmed.",fnum);
show_use();
}
strcpy(pfk -> fk_output,fstring);
}
else /* SHOW function specified */
{
printf(VN); /* display signon message */
show_fun();
}
}
get_fs(string) /* get function string from command tail */
char string[]; /* pointer to character string */
{
char *tail; /* pointer to command tail */
short tcount; /* count of TOTAL characters in command tail */
int slen; /* string length */
tail = 0x80; /* command line is in memory at 0080H */
tcount = *tail++; /* set TOTAL count of characters in command tail */
slen = 0; /* initialize string length */
while(tcount--) /* for all characters in the command tail */
{
if (*tail++ == '"') /* scan for first quotes */
break;
}
if (!tcount) /* no quotes found */
{
printf("\n\007No leading quotes found.");
show_use();
}
++tcount; /* adjust tail count */
while(tcount--) /* for all remaining characters in tail */
{
if (*tail == '"')
{
string[slen] = '\0'; /* add terminator */
break; /* exit from loop */
}
string[slen] = *tail++; /* move char. from tail into string */
if (string[slen] == '<')
string[slen] = 0x0A;
++slen;
}
if (!tcount) /* no terminating quotes found */
{
printf("\n\007No trailing quotes found.");
show_use();
}
return slen; /* return string length */
}
show_fun() /* display settings for all function keys */
{
struct _fkt *pfkt; /* local pointer to function keys */
int count; /* count to access function keys */
char *lf; /* pointer to '<' character (line feed) */
pfkt = get_cba(CB_FKT); /* set pointer to function key table */
for (count = 0; count <= FK_ENTRIES; count++)
{
if (pfkt -> fk_input[0]) /* key is programmed */
{
/* check if at physical end of table */
if (pfkt -> fk_input == 0xFF)
break; /* yes - break out of for-loop */
strcpy(fstring,pfkt -> fk_output);
/* convert all 0x0A chars to '<' */
while (lf = strscn(fstring,"\012"))
{
*lf = '<';
}
printf("\n\tKey #%d = '%s'",count,fstring);
}
++pfkt; /* move to next entry */
}
}
show_use()
{
printf("\nFUNKEY sets a specific function key string.");
printf("\n\tFUNKEY key-number \042string to be programmed<\042 ");
printf("\n\t (Note : '<' is changed to line feed.)");
printf("\n\t ( key-number is from 0 to %d.)",
FK_ENTRIES-1);
printf("\n\t ( string can be up to %d chars.)",
FK_LENGTH);
printf("\n\tFUNKEY SHOW (displays settings for all keys)");
exit();
}


File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
#define LIBVN "1.0" /* Library Version Number */
/* 12:36 02/21/83 */
/* This file contains groups of useful definitions.
It should be included at the front of any programs
that use the functions in BLIB. */
/* Definitions to make minor language modifications to C. */
#define short char /* Short is not supported directly */
/* one of the functions (bv_make) in the library uses the BDS C
function, alloc, to allocate memory. The following definitions
are provided for alloc. */
struct _header /* header for block of memory allocated */
{
struct _header *_ptr; /* pointer to the next header in the chain */
unsigned _size; /* number of bytes in the allocated block */
};
struct _header _base; /* declare the first header of the chain */
struct _header *_allocp; /* used by alloc() and free() functions */
/* BDOS Function Call Numbers. */
#define SETDISK 14 /* Set (Select) Disk */
#define SEARCHF 17 /* Search First */
#define SEARCHN 18 /* Search Next */
#define DELETEF 19 /* Delete File */
#define GETDISK 25 /* Get Default Disk (Currently Logged In) */
#define SETDMA 26 /* Set DMA (Read/Write) address */
#define GETDPARM 31 /* Get Disk Parameter Block address */
#define GETUSER 32 /* Get Current User Number */
#define SETUSER 32 /* Set Current User Number */
/* Direct BIOS Calls.
These definitions are for direct calls into the BIOS.
WARNING - Using these makes program less transportable.
Each symbol is related to its corresponding Jump in the
BIOS Jump Vector.
Only the more useful entries are defined. */
#define CONST 2 /* Console Status */
#define CONIN 3 /* Console Input */
#define CONOUT 4 /* Console Output */
#define LIST 5 /* List Output */
#define AUXOUT 6 /* Auxiliary Output */
#define AUXIN 7 /* Auxiliary Input */
#define HOME 8 /* Home disk */
#define SELDSK 9 /* Select logical disk */
#define SETTRK 10 /* Set track */
#define SETSEC 11 /* Set sector */
#define SETDMA 12 /* Set DMA Address */
#define DREAD 13 /* Disk read */
#define DWRITE 14 /* Disk write */
#define LISTST 15 /* List status */
#define SECTRN 16 /* Sector translate */
#define AUXIST 17 /* Auxiliary Input Status */
#define AUXOST 18 /* Auxiliary Output Status */
/* "Private" entries in Jump Vector */
#define CIOINIT 19 /* Specific Character I/O Initialization */
#define SETDOG 20 /* Set Watchdog timer */
#define CBGADDR 21 /* Configuration Block, Get Address */
/* defines for accessing the Configuration Block */
#define CB_GET 21 /* bios jump number to access routine */
#define DEV_INIT 19 /* bios jump to do device initialization */
#define CB_DATE 0 /* date in ASCII */
#define CB_TIMEA 1 /* time in ASCII */
#define CB_DTFLAGS 2 /* date, time flags */
#define TIME_SET 0x01 /* this bit NZ means date has been set */
#define DATE_SET 0x02 /* this bit NZ means time has been set */
#define CB_FIP 3 /* forced input pointer */
#define CB_SUM 4 /* system startup message */
#define CB_CI 5 /* console input */
#define CB_CO 6 /* console output */
#define CB_AI 7 /* auxiliary input */
#define CB_AO 8 /* auxiliary output */
#define CB_LI 9 /* list input */
#define CB_LO 10 /* list output */
#define CB_DTA 11 /* device table addresses */
#define CB_C1224 12 /* clock 12/24 format flag */
#define CB_RTCTR 13 /* real time clock tick rate (per second) */
#define CB_WDC 14 /* watchdog count */
#define CB_WDA 15 /* watchdog address */
#define CB_FKT 16 /* function key table */
#define CB_COET 17 /* console output escape table */
#define CB_D0_IS 18 /* device 0 initialization stream */
#define CB_D0_BRC 19 /* device 0 baud rate constant */
#define CB_D1_IS 20 /* device 1 initialization stream */
#define CB_D1_BRC 21 /* device 1 baud rate constant */
#define CB_D2_IS 22 /* device 2 initialization stream */
#define CB_D2_BRC 23 /* device 2 baud rate constant */
#define CB_IV 24 /* interrupt vector */
#define CB_LTCBO 25 /* long term config. block offset */
#define CB_LTCBL 26 /* long term config. block length */
#define CB_PUBF 27 /* public files flag */
#define CB_MCBUF 28 /* multi-command buffer */
#define CB_POLLC 29 /* polled console flag */
/* device numbers and names for physical devices */
/* NOTE : Change these definitions for your computer system */
#define T_DEVN 0 /* Terminal */
#define M_DEVN 1 /* Modem */
#define P_DEVN 2 /* Printer */
#define MAXPDEV 2 /* maximum physical device number */
/* names for the physical devices */
#define PN_T "TERMINAL"
#define PN_M "MODEM"
#define PN_P "PRINTER"
/* structure and definitions for function keys */
#define FK_ILENGTH 2 /* number of chars. input when f-key pressed
NOTE : This does NOT include the ESCAPE */
#define FK_LENGTH 16 /* length of string (not including fk_term) */
#define FK_ENTRIES 18 /* number of function key entries in table */
struct _fkt /* function key table */
{
char fk_input[FK_ILENGTH]; /* lead-in character is not in table */
char fk_output[FK_LENGTH]; /* output character string */
char fk_term; /* safety terminating character */
};
/* definitions and structure for device tables */
/* protocol bits */
/* Note : if the most significant bit is
set = 1, then the set_proto function
will logically OR in the value. This
permits Input DTR to co-exist with
Xon or Etx protocol. */
#define DT_ODTR 0x8004 /* output dtr high to send (OR'ed in) */
#define DT_OXON 0x0008 /* output xon */
#define DT_OETX 0x0010 /* output etx/ack */
#define DT_IRTS 0x8040 /* input RTS (OR'ed in) */
#define DT_IXON 0x0080 /* input xon */
#define ALLPROTO 0xDC /* all protocols combined */
struct _dt /* device table */
{
char dt_f1[14]; /* filler */
char dt_st1; /* status byte 1 - has protocol flags */
char dt_st2; /* status byte 2 */
unsigned dt_f2; /* filler */
unsigned dt_etxml; /* etx/ack message length */
char dt_f3[12]; /* filler */
} ;
/* Values returned by the comp_fname (compare file name) */
#define NAME_EQ 0 /* names equal */
#define NAME_LT 1 /* name less than mask */
#define NAME_GT 2 /* name greater than mask */
#define NAME_NE 3 /* name not equal (and comparison ambiguous) */
/* Structure for Standard CP/M File Control Block */
#define FCBSIZE 36 /* define the overall length of an FCB */
struct _fcb
{
short fcb_disk; /* logical disk (0 = default) */
char fcb_fname[11]; /* file name, type (with attributes) */
short fcb_extent; /* current extent */
unsigned fcb_s12; /* reserved for CP/M */
short fcb_reccnt; /* record count used in current extent */
union /* allocation blocks can be either */
{ /* single or double bytes */
short fcbab_short[16];
unsigned fcbab_long[8];
} _fcbab;
short fcb_currec; /* current record within extent */
char fcb_ranrec[3]; /* record for random read/write */
};
/* parameter block used for calls to the directory management routines */
#define DIR_BSZ 128 /* directory buffer size */
struct _dirpb
{
short dp_open; /* 0 to request directory to be opened */
short dp_end; /* NZ when at end of directory */
short dp_write; /* NZ to write current sector to disk */
struct _dir *dp_entry; /* pointer to directory entry in buffer */
char dp_buffer [DIR_BSZ]; /* directory sector buffer */
char dp_disk; /* current logical disk */
int dp_track; /* start track */
int dp_sector; /* start sector */
int dp_nument; /* number of directory entries */
int dp_entrem; /* entries remaining to process */
int dp_sptrk; /* number of sectors per track */
int dp_nabpde; /* number of allocation blocks per dir. entry */
unsigned dp_nab; /* number of allocation blocks */
int dp_absize; /* allocation block size (in Kbytes) */
};
/* The err_dir function is used to report errors found by the
directory management routines, open_dir and rw_dir.
Err_dir needs an parameter to define the operation being
performed when the error occurred. The following #defines
represent the operations possible. */
#define W_DIR 0 /* Writing Directory */
#define R_DIR 1 /* Reading Directory */
#define O_DIR 2 /* Opening Directory */
/* disk parameter block maintained by CPM */
struct _dpb
{
unsigned dpb_sptrk; /* sectors per track */
short dpb_bshift; /* block shift */
short dpb_bmask; /* block mask */
short dpb_emask; /* extent mask */
unsigned dpb_maxabn; /* maximum allocation block number */
unsigned dpb_maxden; /* maximum directory entry number */
short dpb_rab0; /* allocation blocks reserved for */
short dpb_rab1; /* directory blocks */
unsigned dpb_diskca; /* disk changed workarea */
unsigned dpb_trkoff; /* track offset */
};
/* disk directory entry format */
struct _dir {
char de_userno; /* user number or 0xE5 if free entry */
char de_fname[11]; /* file name [8] and type [3] */
int de_extent; /* extent number of this entry */
int de_reccnt; /* number of 128-byte records used in last
allocation block */
union /* allocation blocks can be either */
{ /* single or double bytes */
short de_short[16];
unsigned de_long[8];
} _dirab;
};
/* disk request parameters for BIOS-level Read/Writes */
struct _drb
{
short dr_disk; /* logical disk A = 0, B = 1... */
unsigned dr_track; /* track (for SETTRK) */
unsigned dr_sector; /* sector (for SETSEC) */
char *dr_buffer; /* buffer address (for SETDMA) */
} ;
/* search control block used by directory scanning functions */
struct _scb
{
short scb_userno; /* user number(s) to match */
char scb_fname[11]; /* file name and type */
short scb_extent; /* extent number */
char unused[19]; /* dummy bytes to make this appear like
a file control block */
short scb_length; /* number of bytes to compare */
short scb_disk; /* current disk to be searched */
unsigned scb_adisks; /* bit map of disks to be searched.
The right-most bit is for disk A:. */
} ;
/* code table related definitions */
#define CT_SNF 0xFFFF /* String Not Found */
struct _ct /* define structure of code table */
{
unsigned _ct_code; /* code value */
char *_ct_sp; /* string pointer */
};
/* Structure for bit-vectors */
struct _bv
{
unsigned bv_bytes; /* number of bytes in the vector */
char *bv_bits; /* pointer to the first byte in the vector */
char *bv_end; /* pointer to byte following bit vector */
} ;
/* End of LIBRARY.H */


View File

@@ -0,0 +1,187 @@
#define VN "1.0 02/12/83"
/* MAKE - This utility is really two very similar programs
according to the parameter specified on the command line.
INVISIBLE finds all of the specified files and moves them
to user number 0 and sets them to be System and Read Only
status. These files can then be accessed from user numbers
other than 0 when the public files feature is enabled in the
BIOS.
VISIBLE is the "opposite" in that the specified files are
moved to the current user number and changed to Directory
and Read Write status. */
#include <LIBRARY.H>
struct _dirpb dir_pb; /* directory management parameter block */
struct _dir *dir_entry; /* pointer to directory entry */
struct _scb scb; /* search control block */
short to_user; /* user number to which files will be set */
short from_user; /* user number from which files will be moved */
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
short name_flag; /* NZ to display names of files moved */
short cur_disk; /* current logical disk at start of program */
int mcount; /* match count (no. of file names matched) */
short invisible; /* NZ when parameter specifies Invisible */
char *operation; /* pointer to either "Invisible" or "Visible" */
main(argc,argv)
short argc; /* argument count */
char *argv[]; /* argument vector (pointer to an array of chars) */
{
printf("\nMAKE Version %s (Library %s)",VN,LIBVN);
chk_use(argc); /* check usage */
cur_disk = bdos(GETDISK); /* get current default disk */
mcount = 0; /* initialize count */
/* set the invisible flag according to the parameter */
if (usstrcmp("VISIBLE",argv[2]) == 0)
invisible = 0;
else if (usstrcmp("INVISIBLE",argv[2]) == 0)
invisible = 1;
else
{
printf("\n\007Error : '%s' can only be INVISIBLE/VISIBLE.",argv[2]);
exit();
}
/* set the from_user and to_user numbers depending on which
program is to be built, and the parameters specified. */
if (invisible)
{
from_user = bdos(GETUSER,0xFF); /* get current user number */
to_user = 0; /* always move files to user 0 */
operation = "Invisible"; /* set pointer to string */
}
else /* visible */
{
from_user = 0; /* always move from user 0 */
to_user = bdos(GETUSER,0xFF); /* get current user */
operation = "Visible"; /* set pointer to string */
}
/* set search control block disks, name, type, user number,
extent number and number of bytes to compare - in this
case, match the 'from' user, all extents. */
setscb(scb,argv[1],from_user,'?',13); /* set disks, name, type */
name_flag = usstrcmp("NAMES",argv[3]); /* set name suppress flag from param. 3 */
/* to simplify the logic below, name_flag must be made
NZ if it is equal to NAME_EQ, 0 if it is any other value */
name_flag = (name_flag == NAME_EQ ? 1 : 0);
/* convert search user number and name for output */
conv_dfname(scb.scb_disk,scb,file_name);
printf("\n\nMoving files from User %d to %d and making them %s.",
from_user,to_user,operation);
for (scb.scb_disk = 0; /* starting with logical disk A: */
scb.scb_disk < 16; /* until logical disk P: */
scb.scb_disk++) /* move to next logical disk */
{
/* check if current disk has been selected for search */
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
continue; /* no - so bypass this disk */
printf("\nSearching disk : %c",(scb.scb_disk + 'A'));
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
if (name_flag) /* if file names are to be displayed */
putchar('\n'); /* move to column 1 */
/* set the directory to "closed", and force the get_nde
function to open it. */
dir_pb.dp_open = 0;
/* while not at the end of the directory,
set a pointer to the next directory entry. */
while(dir_entry = get_nde(dir_pb))
{
/* match those entries that have the correct
user number, file name, type, and any
extent number. */
if (
(dir_entry -> de_userno != 0xE5) &&
(comp_fname(scb,dir_entry) == NAME_EQ)
)
{
mcount++; /* update matched counts */
if (invisible)
{ /* set ms bits */
dir_entry -> de_fname[8] |= 0x80;
dir_entry -> de_fname[9] |= 0x80;
}
else /* visible */
{ /* clear ms bits */
dir_entry -> de_fname[8] &= 0x7F;
dir_entry -> de_fname[9] &= 0x7F;
}
/* move to correct user number */
dir_entry -> de_userno = to_user;
/* indicate sector to be written back */
dir_pb.dp_write = 1;
/* check if name to be displayed */
if (name_flag)
{
conv_dfname(scb.scb_disk,dir_entry,file_name);
printf("\n\t%s made %s in User %d.",
file_name,operation,to_user);
}
}
} /* all directory entries processed */
} /* all disks processed */
if (mcount == 0)
printf("\n --- No Files Processed --- ");
bdos(SETDISK,cur_disk); /* reset to current disk */
}
chk_use(argc) /* check usage */
/* This function checks that the correct number of
parameters has been specified, outputting instructions
if not.
*/
/* Entry Parameter */
int argc; /* Count of the number of arguments on the command line */
{
/* The minimum value of argc is 1 (for the program name itself),
so argc is always one greater than the number of parameters
on the command line */
if (argc == 3 || argc == 4)
return;
else
{
printf("\nUsage :");
printf("\n\tMAKE d:filename.typ INVISIBLE {NAMES}");
printf("\n\t VISIBLE");
printf("\n\t *:filename.typ (All disks)");
printf("\n\t ABCD..OP:filename.typ (Selected Disks)");
printf("\n\tNAMES option shows names of files processed.");
exit();
}
}


View File

@@ -0,0 +1,182 @@
#define VN "1.0 02/10/83"
/* MOVE - This utility moves file(s) from one user number to another,
but on the SAME logical disk. Files are not actually copied -
rather their directory entries are changed. */
#include <LIBRARY.H>
struct _dirpb dir_pb; /* directory management parameter block */
struct _dir *dir_entry; /* pointer to directory entry */
struct _scb scb; /* search control block */
#define DIR_BSZ 128 /* directory buffer size */
char dir_buffer[DIR_BSZ]; /* directory buffer */
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
short name_flag; /* NZ to display names of files moved */
short cur_disk; /* current logical disk at start of program */
int from_user; /* user number from which to move files */
int to_user; /* user number to which files will be moved */
int mcount; /* match count (no. of file names matched) */
int dmcount; /* per disk match count */
int lcount; /* line count (for lines displayed) */
main(argc,argv)
short argc; /* argument count */
char *argv[]; /* argument vector (pointer to an array of chars) */
{
printf("\nMOVE Version %s (Library %s)",VN,LIBVN);
chk_use(argc); /* check usage */
to_user = atoi(argv[2]); /* convert user no. to integer */
/* set and chk destination user number */
if(to_user > 15)
{
printf("\nError - the destination user number can not be greater than 15.");
}
/* set the current user number */
from_user = bdos(GETUSER,0xFF);
/* check if source user number specified */
if (isdigit(argv[3][0]))
{
/* set and check source user number */
if((from_user = atoi(argv[3])) > 15)
{
printf("\nError - the source user number can not be greater than 15.");
exit();
}
/* set name suppress flag from parameter #4 */
name_flag = usstrcmp("NAMES",argv[4]);
}
else /* no source user specified */
{
/* set name suppress flag from parameter #3 */
name_flag = usstrcmp("NAMES",argv[3]);
}
/* to simplify the logic below, name_flag must be made
NZ if it is equal to NAME_EQ, 0 if it is any other value */
name_flag = (name_flag == NAME_EQ ? 1 : 0);
if (to_user == from_user) /* to = from */
{
printf("\nError - 'to' user number is the same as the 'from' user number.");
exit();
}
/* set the search control block file name, type, user number
extent number and length - length matches user number, file
name and type. As the extent number does not enter into the
comparison, all extents of a given file will be found. */
setscb(scb,argv[1],from_user,'?',13);
cur_disk = bdos(GETDISK); /* get current default disk */
lcount = dmcount = mcount = 0; /* initialize counts */
for (scb.scb_disk = 0; /* starting with logical disk A: */
scb.scb_disk < 16; /* until logical disk P: */
scb.scb_disk++) /* move to next logical disk */
{
/* check if current disk has been selected for search */
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
continue; /* no - so bypass this disk */
/* convert search user number and name for output */
conv_dfname(scb.scb_disk,scb,file_name);
printf("\n\nMoving file(s) %s -> User %d.",file_name,to_user);
lcount++; /* update line count */
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
dmcount = 0; /* reset disk matched count */
if (name_flag) /* if file names are to be displayed */
putchar('\n'); /* move to column 1 */
/* set the directory to "closed" to force the get_nde
function to open it. */
dir_pb.dp_open = 0;
/* while not at the end of the directory, set a pointer
to the next directory entry */
while(dir_entry = get_nde(dir_pb))
{
/* match those entries that have the correct
user number, file name, type, and any
extent number. */
if (
(dir_entry -> de_userno != 0xE5) &&
(comp_fname(scb,dir_entry) == NAME_EQ)
)
{
dir_entry -> de_userno = to_user; /* move to new user */
/* request sector to be written back */
dir_pb.dp_write = 1;
mcount++; /* update matched counts */
dmcount++; /* per disk count */
if (name_flag) /* check map option */
{
conv_dfname(scb.scb_disk,dir_entry,file_name);
printf("%s ",file_name);
/* check if need to start new line */
if (!(dmcount % 4))
{
putchar('\n');
if (++lcount > 18)
{
lcount = 0;
printf("\nPress Space Bar to continue....");
getchar();
putchar('\n');
}
}
}
}
}
}
if (mcount == 0)
printf("\n --- No Files Moved --- ");
bdos(SETDISK,cur_disk); /* reset to current disk */
}
chk_use(argc) /* check usage */
/* This function checks that the correct number of
parameters has been specified, outputting instructions
if not. */
/* Entry Parameter */
int argc; /* Count of the number of arguments on the command line */
{
/* The minimum value of argc is 1 (for the program name itself),
so argc is always one greater than the number of parameters
on the command line */
if (argc == 1 || argc > 5)
{
printf("\nUsage :");
printf("\n\tMOVE d:filename.typ to_user {from_user} {NAMES}");
printf("\n\t *:filename.typ (All disks)");
printf("\n\t ABCD..OP:filename.typ (Selected Disks)");
printf("\n\tNAMES option shows names of files moved.");
exit();
}
}


View File

@@ -0,0 +1,5 @@
PIP $1:=$2:FIG*.*[V
PIP $1:=$2:*.C[V
PIP $1:=$2:READ.ME[V
MOVEF $1:*.* 0


View File

@@ -0,0 +1,264 @@
#define VN "\nPROTOCOL Vn 1.0 02/17/83"
/* PROTOCOL - This utility sets the serial port protocol for the
specified physical device. Alternatively, it displays the
current protocols for all of the serial devices. */
#include <LIBRARY.H>
/* code tables used to relate ASCII strings to code values */
struct _ct ct_iproto[3]; /* code table for input protocols */
struct _ct ct_oproto[4]; /* code table for output protocols */
struct _ct ct_dproto[7]; /* code table for displaying protocols */
struct _ct ct_pdev[MAXPDEV + 2]; /* physical device table */
struct _ct ct_io[3]; /* input, output */
/* parameters on the command line */
#define PDEV argv[1] /* physical device */
#define IO argv[2] /* input/output */
#define PROTO argv[3] /* protocol */
#define PROTOL argv[4] /* protocol message length */
main(argc,argv)
int argc;
char *argv[];
{
printf(VN); /* display signon message */
setup(); /* setup code tables */
chk_use(argc); /* check correct usage */
/* check if request to show current settings */
if (usstrcmp("SHOW",argv[1]))
{ /* no, assume a set is required */
set_proto(get_pdev(PDEV), /* physical device */
/* Input/Output and Protocol */
get_proto(get_io(IO),PROTO),
PROTOL); /* Protocol message length */
}
show_proto();
} /* end of program */
setup() /* setup the code tables for this program */
{
/* initialize the physical device table */
ct_init(ct_pdev[0],0,PN_T); /* terminal */
ct_init(ct_pdev[1],1,PN_P); /* printer */
ct_init(ct_pdev[2],2,PN_M); /* modem */
ct_init(ct_pdev[3],CT_SNF,"*"); /* terminator */
/* initialize the input/output table */
ct_init(ct_io[0],0,"INPUT");
ct_init(ct_io[1],1,"OUTPUT");
ct_init(ct_io[2],CT_SNF,"*"); /* terminator */
/* initialize the output protocol table */
ct_init(ct_oproto[0],DT_ODTR,"DTR");
ct_init(ct_oproto[1],DT_OXON,"XON");
ct_init(ct_oproto[2],DT_OETX,"ETX");
ct_init(ct_oproto[3],CT_SNF,"*"); /* terminator */
/* initialize the input protocol table */
ct_init(ct_iproto[0],DT_IRTS,"RTS");
ct_init(ct_iproto[1],DT_IXON,"XON");
ct_init(ct_iproto[2],CT_SNF,"*"); /* terminator */
/* initialize the display protocol */
ct_init(ct_dproto[0],DT_ODTR,"Output DTR");
ct_init(ct_dproto[1],DT_OXON,"Output XON");
ct_init(ct_dproto[2],DT_OETX,"Output ETX");
ct_init(ct_dproto[3],DT_IRTS,"Input RTS");
ct_init(ct_dproto[4],DT_IXON,"Input XON");
ct_init(ct_dproto[5],CT_SNF,"*");
}
unsigned
get_pdev(ppdev) /* get physical device */
/* This function returns the physical device code
specified by the user in the command line. */
char *ppdev; /* pointer to character string */
{
unsigned retval; /* return value */
retval = ct_parc(ct_pdev,ppdev); /* get code for ASCII string */
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Physical Device '%s' is invalid or ambiguous.",
ppdev);
printf("\nLegal Physical Devices are : ");
ct_disps(ct_pdev); /* display all values */
exit();
}
return retval; /* return code */
}
unsigned
get_io(pio) /* get input/output parameter */
char *pio; /* pointer to character string */
{
unsigned retval; /* return value */
retval = ct_parc(ct_io,pio); /* get code for ASCII string */
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Input/Output direction '%s' is invalid or ambiguous.",
pio);
printf("\nLegal values are : ");
ct_disps(ct_io); /* display all values */
exit();
}
return retval; /* return code */
}
unsigned
get_proto(output,pproto)
/* This function returns the protocol code for the
protocol specified by the user in the command line. */
int output; /* =1 for output, =0 for input */
char *pproto; /* pointer to character string */
{
unsigned retval; /* return value */
if (output) /* OUTPUT specified */
{
/* get code for ASCII string */
retval = ct_parc(ct_oproto,pproto);
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Output Protocol '%s' is invalid or ambiguous.",
pproto);
printf("\nLegal Output Protocols are : ");
ct_disps(ct_oproto); /* display valid protocols */
exit();
}
}
else /* INPUT specified */
{
/* get code for ASCII string */
retval = ct_parc(ct_iproto,pproto);
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Input Protocol '%s' is invalid or ambiguous.",
pproto);
printf("\nLegal Input Protocols are : ");
ct_disps(ct_iproto); /* display valid protocols */
exit();
}
}
return retval; /* return code */
}
set_proto(pdevc,protoc,pplength) /* set the protocol for physical device */
int pdevc; /* physical device code */
unsigned protoc; /* protocol byte */
char *pplength; /* pointer to protocol length */
{
struct _ppdt
{
char *pdt[16]; /* array of 16 pointers to the device tables */
} ;
struct _ppdt *ppdt; /* pointer to the device table array */
struct _dt *dt; /* pointer to a device table */
ppdt = get_cba(CB_DTA); /* set pointer to array of pointers */
dt = ppdt -> pdt[pdevc];
if (!dt) /* chk if pointer in array is valid */
{
printf("\nError - Array of Device Table Addresses is not set for device #%d.",
pdevc);
exit();
}
if (protoc & 0x8000) /* check if protocol byte to be set
directly or to be OR'ed in */
{ /* OR'ed */
dt -> dt_st1 |= (protoc & 0x7F);
}
else
{ /* set directly */
dt -> dt_st1 = (protoc & 0x7F);
}
if ((protoc & 0x7F) == DT_OETX) /* if etx/ack, check for message
length */
{
if (isdigit(*pplength)) /* check if length present */
{
/* convert length to binary and set device
table field. */
dt -> dt_etxml = atoi(pplength);
}
}
}
show_proto() /* show the current protocol settings */
{
struct _ppdt
{
char *pdt[16]; /* array of 16 pointers to the device tables */
} ;
struct _ppdt *ppdt; /* pointer to the device table array */
struct _dt *dt; /* pointer to a device table */
int pdevc; /* physical device code */
struct _ct *dproto; /* pointer to display protocols */
ppdt = get_cba(CB_DTA); /* set pointer to array of pointers */
/* for all physical devices */
for (pdevc = 0; pdevc <= MAXPDEV; pdevc++)
{
/* set pointer to device table */
dt = ppdt -> pdt[pdevc];
if (dt) /* chk if pointer in array is valid */
{
printf("\n\tProtocol for %s - ",ct_strc(ct_pdev,pdevc));
/* check if any protocols set */
if (!(dt -> dt_st1 & ALLPROTO))
{
printf("None.");
continue;
}
/* set pointer to display protocol table */
dproto = ct_dproto;
while (dproto -> _ct_code != CT_SNF)
{
/* check if protocol bit set */
if (dproto -> _ct_code & dt -> dt_st1)
{ /* display protocol */
printf("%s ",dproto -> _ct_sp);
}
++dproto; /* move to next entry */
}
/* chk if etx/ack protocol and
message length to be displayed */
if (dt -> dt_st1 & DT_OETX)
printf(" Message Length %d bytes.",
dt -> dt_etxml);
}
}
}
chk_use(argc) /* check for correct usage */
int argc; /* argument count on commmand line */
{
if (argc == 1)
{
printf("\nPROTOCOL sets the physical device's serial protocols.");
printf("\n\tPROTOCOL physical-device direction protocol {message-length}");
printf("\n\nLegal physical devices are :");
ct_disps(ct_pdev);
printf("\nLegal direction/protocols are :");
ct_disps(ct_dproto);
printf("\n\tMessage length can be specifed with Output Etx.\n");
exit();
}
}


View File

@@ -0,0 +1,88 @@
[READ.ME]
The Programmer's CP/M Handbook Source Code Examples
Version 1.0 August 18, 1983
(c) 1983 Johnson-Laird Inc.
W<EFBFBD> hav<61> trie<69> t<> includ<75> a<> man<61> o<> th<74> example<6C> fro<72> th<74> CP/<2F> <20>
Programmer'<27> Handboo<6F> a<> wa<77> possibl<62> t<> fi<66> ont<6E> 2 single-sided
single-density diskettes (or alternately, <20> single-densit<69> <20>
double-side<64> "flippy" diskette)<29> Ou<4F> origina<6E> intentio<69> wa<77> t<> <20>
includ<EFBFBD> th<74> sourc<72> cod<6F> fo<66> ever<65> figur<75> i<> th<74> boo<6F> a<> wel<65> a<> al<61> <20>
th<EFBFBD> exampl<70> listing<6E> i<> Chapte<74> 5<> Sinc<6E> thi<68> prove<76> t<> b<> impossibl<62> <20>
t<EFBFBD> fi<66> ont<6E> tw<74> diskettes<65> w<> hav<61> chose<73> thos<6F> figure<72> an<61> example<6C> <20>
whic<EFBFBD> w<> fel<65> woul<75> b<> th<74> mos<6F> useful<75> Th<54> onl<6E> majo<6A> omissio<69> i<> <20>
Figur<EFBFBD> 9-<2D> whic<69> deal<61> wit<69> ba<62> secto<74> management<6E>
W<EFBFBD> hav<61> fixe<78> on<6F> bu<62> an<61> adde<64> on<6F> lin<69> t<> Figur<75> 8-10<31> Thi<68> i<> <20>
note<EFBFBD> i<> th<74> sourc<72> code<64> Th<54> line<6E> whic<69> wer<65> lef<65> ou<6F> i<> th<74> boo<6F> <20>
afte<EFBFBD> pag<61> 26<32> ar<61> als<6C> included<65> Yo<59> ma<6D> notic<69> som<6F> mino<6E> <20>
difference<EFBFBD> i<> capitalization<6F> indentation<6F> etc<74> i<> th<74> comment<6E> wher<65> <20>
w<EFBFBD> hav<61> no<6E> include<64> change<67> mad<61> i<> copyediting.
W<EFBFBD> woul<75> appreciat<61> hearin<69> fro<72> you<6F> especiall<6C> abou<6F> an<61> bugs<67> <20>
typos<EFBFBD> othe<68> horribl<62> goofs<66> suggestion<6F> fo<66> improvements.
Contents of Diskette No 1
Filename Page No. Figure Title
FIG5-2.AS<41> 70 Equate<74> fo<66> BDO<44> functio<69> cod<6F> numbers
FIG5-3.ASM 74 Write console byte example, output null-byte
terminated message from specified address
FIG5-4.AS͠ 7<><37> Writ<69><74>consol<6F><6C>byt<79><74>example<6C><65>outpu<70><75>null-byt<79>
terminate<74> messag<61> followin<69> cal<61> t<> subroutine
FIG5-5.ASM 76 Read line from reader device
FIG5-6.ASM 78 Write line to punch device
FIG5-7.ASM 79 Write line to list device
FIG5-8.AS<41> 8<> Read/Writ<69> strin<69> from/t<> consol<6F> usin<69> ra<72> I/O
FIG5-10.ASM 86 IOBYTE equates
FIG5-11.ASM 87 Simple terminal emulator
FIG5-12.ASM 89 Display $-terminated message on console
FIG5-13.ASM 92 Read console string for keyboard options
FIG5-14.ASM 95 Determine the CP/M version number
FIG5-15.ASM 96 Reset requested disk drive
FIG5-16.ASM 100 Open file request
FIG5-17.AS<41> 10<31> Searc<72> first/nex<65> call<6C> fo<66> ambiguou<6F> filenam<61>
FIG5-18.ASM 110 Read next character from sequential disk file
FIG5-19.ASM 113 Write next character to sequential disk file
FIG5-20.ASM 115 Create file request
FIG5-21.ASM 117 Rename file request
FIG5-22.ASM 122 Set file attributes
FIG5-23.ASM 123 Get file attributes
FIG5-24.ASM 126 Accessing disk parameter block data
FUNCTN33.ASM 131 Example for function 33, read random
FIG5-25.ASM 135 Create random file
FIG5-26.ASM 136 Read/write variable length records randomly
FIG6-4.ASM 159 Simple BIOS listing
<EFBFBD>FIG7-5.ASM 191 Example PUTCP/M
FIG7-7.ASM 198 Example CP/M cold bootstrap loader
FIG8-6.ASM 226 Device table equates
FIG10-5.ASM 363 Testbed for real time clock driver in the BIOS
FIG10-6.ASM 365 Testbed for disk I/O drivers in the BIOS
ERASE.<2E> 41<34> Figur<75> 11-3<> request<73> confirmation before erasing
UNERASE.C 412 Figure 11-4, "revives" erased files
FIND.C 417 Figure 11-5, locates specific files or groups of files
SPACE.<2E> 42<34> Figur<75> 11-6<> display<61> ho<68> muc<75> dis<69> storage is used
or available
MOVE.C 424 Figure 11-7, "moves" files from one user to another
MAKE.C 428 Figure 11-8, makes files "invisible" and protected
or makes them "visible," accessible and unprotected
SPEED.C 431 Figure 11-9, sets the baud rate for a specific device
PROTOCOL.C 435 Figure 11-10, sets the protocol governing input and output
of a specified serial device
ASSIGN.C 439 Figure 11-11, assigns a logical devices input and output
to two or more physical devices
DATE.C 443 Figure 11-12, makes the current date part of the system
TIME.C 444 Figure 11-13, makes the current time part of the system
FUNKEY.C 446 Figure 11-14, sets the character strings associated with
specific function keys
Contents of Diskette 2
FIG8-10.ASM 237 Enhanced BIOS listing
FIG9-5.ASM 312 User-friendly disk error processor
FIG10-2.ASM 331 Debug subroutines
FIG10-4.ASM 355 Testbed for character I/O drivers
LIBRARY.C 372 Figure 11-1, commonly used functions in C
LIBRARY.H 390 Figure 11-2, code to be included at the beginning of
any program that calls LIBRARY functions in Figure 11-1

View File

@@ -0,0 +1,229 @@
#define VN "1.0 02/11/83"
/* SPACE - This utility displays a map showing on the amount of space
(expressed as relative percentages) occupied in each user number
for each logical disk). It also shows the relative amount of space
free. */
#include <LIBRARY.H>
struct _dirpb dir_pb; /* directory management parameter block */
struct _dir *dir_entry; /* pointer to directory entry */
struct _scb scb; /* search control block */
struct _dpb dpb; /* CP/M's disk parameter block */
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
short cur_disk; /* current logical disk at start of program
NZ = show map of number of files */
int count; /* used to access the allocation block numbers
in each directory entry */
int user; /* used to access the disk map when calculating */
/* The array below is used to tabulate the results for each
disk drive, and for each user number on the drive.
In addition, two extra "users" have been added for "Free"
and "Used" values.
*/
unsigned disk_map[16][18]; /* Disk A -> P, Users 0 -> 15, Free, Used */
#define USED_COUNT 16 /* "user" number for Used entities */
#define FREE_COUNT 17 /* "user" number for Free entities */
main(argc,argv)
short argc; /* argument count */
char *argv[]; /* argument vector (pointer to an array of chars) */
{
printf("\nSPACE Version %s (Library %s)",VN,LIBVN);
chk_use(argc); /* check usage */
cur_disk = bdos(GETDISK); /* get current default disk */
dm_clr(disk_map); /* reset disk map */
ssetscb(scb,argv[1]); /* special version : set disks,
name, type */
for (scb.scb_disk = 0; /* starting with logical disk A: */
scb.scb_disk < 16; /* until logical disk P: */
scb.scb_disk++) /* move to next logical disk */
{
/* check if current disk has been selected for search */
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
continue; /* no - so bypass this disk */
printf("\nSearching disk : %c",(scb.scb_disk + 'A'));
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
/* set the directory to "closed", and force the get_nde
function to open it. */
dir_pb.dp_open = 0;
/* while not at the end of the directory, set a pointer
to the next entry in the directory */
while (dir_entry = get_nde(dir_pb))
{
if (dir_entry -> de_userno == 0xE5)
continue; /* bypass inactive entries */
for (count = 0; /* start with the first alloc. block */
count < dir_pb.dp_nabpde; /* for number of alloc. blks per dir. entry */
count++)
{
if (dir_pb.dp_nabpde == 8) /* assume 8 2-byte numbers */
{
disk_map[scb.scb_disk][dir_entry -> de_userno]
+= (dir_entry -> _dirab.de_long[count] > 0 ? 1 : 0);
}
else /* assume 16 1-byte numbers */
{
disk_map[scb.scb_disk][dir_entry -> de_userno]
+= (dir_entry -> _dirab.de_short[count] > 0 ? 1 : 0);
}
} /* all allocation blocks processed */
} /* end of directory for this disk */
/* Compute the storage used by multiplying the number of
allocation blocks counted by the number of Kbytes in
each allocation block. */
for (user = 0; /* start with user 0 */
user < 16; /* end with user 15 */
user ++) /* move to next user number */
{
/* compute size occupied in Kbytes */
disk_map[scb.scb_disk][user] *= dir_pb.dp_absize;
/* build up sum for this disk */
disk_map[scb.scb_disk][USED_COUNT] += disk_map[scb.scb_disk][user];
}
/* free space = (# of alloc. blks * # of kbyte per blk)
- used Kbytes
- (directory entries * 32) / 1024 ... or divide by 32 */
disk_map[scb.scb_disk][FREE_COUNT] = (dir_pb.dp_nab * dir_pb.dp_absize)
- disk_map[scb.scb_disk][USED_COUNT]
- (dir_pb.dp_nument >> 5); /* same as / 32 */
} /* all disks processed */
printf("\n Numbers show space used in kilobytes.");
printf("\n --- User Numbers --- Space (Kb)");
dm_disp(disk_map,scb.scb_adisks); /* display disk map */
bdos(SETDISK,cur_disk); /* reset to current disk */
}
ssetscb(scb,ldisks) /* special version of - set search control block */
/* This function sets up a search control block according
to just the logical disks specified. The disk are specified as
a single string of characters without any separators. An
asterisk means "all disks". For example -
ABGH (disks A:, B:, G: and H: )
* (all disks for which SELDSK has tables)
It sets the bit map according to which disks should be searched.
For each selected disk, it checks to see if an error is generated
when selecting the disk (i.e. if there are disk tables in the BIOS
for the disk).
The file name, type and extent number are all set to '?' to match
all possible entries in the directory. */
/* Entry Parameters */
struct _scb *scb; /* pointer to search control block */
char *ldisks; /* pointer to the logical disks */
/* Exit Parameters
None.
*/
{
int disk; /* disk number currently being checked */
unsigned adisks; /* bit map for active disks */
adisks = 0; /* assume no disks to search */
if (*ldisks) /* some values specified */
{
if (*ldisks == '*') /* check if "all disks" */
{
adisks = 0xFFFF; /* set all bits */
}
else /* set specific disks */
{
while(*ldisks) /* until end of disks reached */
{
/* build the bit map by getting the next disk
id. (A - P), converting it to a number
in the range 0 - 15, and shifting a 1-bit
left that many places and OR-ing it into
the current active disks.
*/
adisks |= 1 << (toupper(*ldisks) - 'A');
++ldisks; /* move to next character */
}
}
}
else /* use only current default disk */
{
/* set just the bit corresponding to the current disk */
adisks = 1 << bdos(GETDISK);
}
/* set the user number, file name, type and extent to '?'
so that all active directory entries will match */
/* 0123456789012 */
strcpy(&scb -> scb_userno,"?????????????");
/* Make calls to the BIOS SELDSK routine to make sure that
all of the active disk drives indeed do have disk tables
for them in the BIOS. If they don't, turn off the corresponding
bits in the bit map. */
for (disk = 0; /* start with disk A: */
disk < 16; /* until disk P: */
disk++) /* use next disk */
{
if ( !((1 << disk) & adisks) ) /* avoid unnecessary selects */
continue;
if (biosh(SELDSK,disk) == 0) /* make BIOS SELDSK call */
{ /* returns 0 if invalid disk */
/* turn OFF corresponding bit in mask
by ANDing it with bit mask having
all the other bits set = 1. */
adisks &= ((1 << disk) ^ 0xFFFF);
}
}
scb -> scb_adisks = adisks; /* set bit map in scb */
} /* end ssetscb */
chk_use(argc) /* check usage */
/* This function checks that the correct number of
parameters has been specified, outputting instructions
if not. */
/* Entry Parameter */
int argc; /* Count of the number of arguments on the command line */
{
/* The minimum value of argc is 1 (for the program name itself),
so argc is always one greater than the number of parameters
on the command line */
if (argc != 2)
{
printf("\nUsage :");
printf("\n\tSPACE * (All disks)");
printf("\n\tSPACE ABCD..OP (Selected Disks)");
exit();
}
} /* end chk_use */


View File

@@ -0,0 +1,179 @@
#define VN "\nSPEED 1.0 02/17/83"
/* This utility sets the baud rate speed for each of the physical
devices. */
#include <LIBRARY.H>
struct _ct ct_pdev[MAXPDEV + 2]; /* physical device table */
/* hardware specific items */
/* baud rates for serial ports */
#define B300 0x35 /* 300 baud */
#define B600 0x36 /* 600 baud */
#define B1200 0x37 /* 1200 baud */
#define B2400 0x3A /* 2400 baud */
#define B4800 0x3C /* 4800 baud */
#define B9600 0x3E /* 9600 baud */
#define B19200 0x3F /* 19200 baud */
struct _ct ct_br[10]; /* code table for baud rates (+ spare entries) */
/* parameters on the command line */
#define PDEV argv[1] /* physical device */
#define BAUD argv[2] /* baud rate */
main(argc,argv)
int argc;
char *argv[];
{
printf(VN); /* display sign on message */
setup(); /* setup code tables */
chk_use(argc); /* check correct usage */
/* check if request to show current settings */
if (usstrcmp("SHOW",argv[1]))
{ /* No, assume setting is required */
set_baud(get_pdev(PDEV),get_baud(BAUD)); /* set baud rate */
}
show_baud(); /* display current settings */
} /* end of program */
setup() /* setup the code tables for this program */
{
/* initialize the physical device table */
ct_init(ct_pdev[0],T_DEVN,PN_T); /* terminal */
ct_init(ct_pdev[1],P_DEVN,PN_P); /* printer */
ct_init(ct_pdev[2],M_DEVN,PN_M); /* modem */
ct_init(ct_pdev[3],CT_SNF,"*"); /* terminator */
/* initialize the baud rate table */
ct_init(ct_br[0],B300,"300");
ct_init(ct_br[1],B600,"600");
ct_init(ct_br[2],B1200,"1200");
ct_init(ct_br[3],B2400,"2400");
ct_init(ct_br[4],B4800,"4800");
ct_init(ct_br[5],B9600,"9600");
ct_init(ct_br[6],B19200,"19200");
ct_init(ct_br[7],CT_SNF,"*"); /* terminator */
}
unsigned
get_pdev(ppdev) /* get physical device */
/* This function returns the physical device code
specified by the user in the command line. */
char *ppdev; /* pointer to character string */
{
unsigned retval; /* return value */
retval = ct_parc(ct_pdev,ppdev); /* get code for ASCII string */
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Physical Device '%s' is invalid or ambiguous.",
ppdev);
printf("\nLegal Physical Devices are : ");
ct_disps(ct_pdev); /* display all values */
exit();
}
return retval; /* return code */
}
unsigned
get_baud(pbaud)
/* This function returns the baud rate time constant
for baud rate specified by the user in the command line. */
char *pbaud; /* pointer to character string */
{
unsigned retval; /* return value */
retval = ct_parc(ct_br,pbaud); /* get code for ASCII string */
if (retval == CT_SNF) /* if string not found */
{
printf("\n\007Baud Rate '%s' is invalid or ambiguous.",
pbaud);
printf("\nLegal Baud Rates are : ");
ct_disps(ct_br); /* display all values */
exit();
}
return retval; /* return code */
}
set_baud(pdevc,baudc) /* set the baud rate of the specified device */
int pdevc; /* physical device code */
short baudc; /* baud rate code */
/* on some systems this may have to be a
two-byte (unsigned) value. */
{
short *baud_rc; /* pointer to the baud rate constant */
/* on some systems this may have to be a
two-byte (unsigned) value. */
/* Note : the respective codes for accessing the baud rate constants
via the get_cba (get configuration block address) function are:
Device #0 = 19, #1 = 21, #2 = 23. This function uses this
mathematical relationship. */
/* setup pointer to the baud rate constant */
baud_rc = get_cba(CB_D0_BRC + (pdevc << 1));
/* then set the baud rate constant */
*baud_rc = baudc;
/* then call the BIOS initialization routine */
bios(CIOINIT,pdevc);
}
show_baud() /* show current baud rate */
{
int pdevn; /* physical device number */
short baudc; /* baud rate code */
/* on some systems this may have to be a
two-byte (unsigned) value. */
short *baud_rc; /* pointer to the baud rate constant */
/* on some systems this may have to be a
two-byte (unsigned) value. */
/* Note : the respective codes for accessing the baud rate constants
via the get_cba (get configuration block address) function are:
Device #0 = 19, #1 = 21, #2 = 23. This function uses this
mathematical relationship. */
printf("\nCurrent Baud Rate settings are :");
for (pdevn = 0; pdevn <= MAXPDEV; pdevn ++) /* all physical devices */
{
/* setup pointer to the baud rate constant -
the code for the get_cba function is computed
by adding the physical device number *2 to
the Baud Rate code for device #0 */
baud_rc = get_cba(CB_D0_BRC + (pdevn << 1));
/* then set the baud rate constant */
baudc = *baud_rc;
printf("\n\t%s set to %s baud.",
ct_strc(ct_pdev,pdevn), /* get ptr to device name */
ct_strc(ct_br,baudc) ); /* get ptr to baud rate */
}
}
chk_use(argc) /* check correct usage */
int argc; /* argument count */
{
if (argc == 1)
{
printf("\nThe SPEED utility sets the baud rate speed for each physical device.");
printf("\nUsage is : SPEED physical-device baud-rate, or");
printf("\n SPEED SHOW (to show current settings)");
printf("\n\nValid physical devices are: ");
ct_disps(ct_pdev);
printf("\nValid baud rates are: ");
ct_disps(ct_br);
exit();
}
}


View File

@@ -0,0 +1,77 @@
#define VN "\nTIME Vn 1.0 02/18/83"
/* This utility accepts the current time from the command tail
validates it, and set the internal system time in the BIOS.
Alternatively, it can be requested just to display the current
system time. */
#include <LIBRARY.H>
char *time; /* pointer to the time in the config. block */
char *time_set; /* pointer to the time set flag */
int hh,mm,ss; /* variables to hold hours, minutes, seconds */
int mcount; /* match count of numeric values entered */
int count; /* count used to add leading 0's to time */
main(argc,argv)
int argc;
char *argv[];
{
printf(VN); /* display signon message */
time = get_cba(CB_TIMEA); /* set pointer to time */
time_flag = get_cba(CB_DTFLAGS); /* set pointer to the time set flag */
hh = mm = ss = 0; /* initialize the time in case seconds or
minutes are not specified */
if (argc != 2) /* check if help requested (or needed) */
show_use(); /* display correct usage and exit */
if (usstrcmp("SHOW",argv[1])) /* check if not SHOW option */
{
/* convert time into hours, minutes, seconds */
mcount = sscanf(argv[1],"%d:%d:%d",&hh,&mm,&ss);
if (!mcount) /* input not numeric */
show_use(); /* display correct usage and exit */
if (hh > 12) /* check valid hours, minutes, seconds */
{
printf("\n\007Hours = %d is illegal.",hh);
show_use(); /* display correct usage and exit */
}
if (mm > 59)
{
printf("\n\007Minutes = %d is illegal.",mm);
show_use(); /* display correct usage and exit */
}
if (ss > 59)
{
show_use(); /* display correct usage and exit */
printf("\n\007Seconds = %d is illegal.",ss);
}
/* convert integers back into formatted string */
sprintf(time,"%2d:%2d:%2d",hh,mm,ss);
time[8] = 0x0A; /* terminate with line feed */
time[9] = '\0'; /* new string terminator */
/* convert " 1: 2: 3" into "01:02:03" */
for (count = 0; count < 7; count+=3)
{
if (time[count] == ' ')
time[count] = '0';
}
/* turn bit on to indicate that the time has been set */
*time_flag |= TIME_SET;
}
printf("\n\tCurrent Time is %s",time);
}
show_use() /* display correct usage and exit */
{
printf("\nTIME sets the system time. Usage is :");
printf("\n\tTIME hh{:mm{:ss}}");
printf("\n\tTIME SHOW (to display current time)\n");
exit();
}


View File

@@ -0,0 +1,282 @@
#define VN "1.0 02/12/83"
/* UNERASE - This does the inverse of ERASE. It revives the specified files,
by changing the first byte of their directory entries from 0xE5
back to the specified user number. */
#include <LIBRARY.H>
struct _dirpb dir_pb; /* directory management parameter block */
struct _dir *dir_entry; /* pointer to directory entry */
struct _scb scb; /* search control block */
struct _scb scba; /* scb setup to match all files */
struct _dpb dpb; /* CP/M's disk parameter block */
struct _bv inuse_bv; /* bit vector for blocks in use */
struct _bv file_bv; /* bit vector for file to be unerased */
struct _bv extents; /* bit vector for those extents unerased */
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
short cur_disk; /* current logical disk at start of program
NZ = show map of number of files */
int count; /* used to access the allocation block numbers
in each directory entry */
int user; /* user in which the file is to be revived */
main(argc,argv)
short argc; /* argument count */
char *argv[]; /* argument vector (pointer to an array of chars) */
{
printf("\nUNERASE Version %s (Library %s)",VN,LIBVN);
chk_use(argc); /* check usage */
cur_disk = bdos(GETDISK); /* get current default disk */
/* using a special version of set search control block,
set the disk, name, type (no ambiguous names), the user number
to match only erased entries, and the length to compare
the user, name and type.
This special version also returns the disk_id taken from
the file name on the command line. */
if ((dir_pb.dp_disk = ssetscb(scb,argv[1],0xE5,12)) == 0)
{ /* use default disk */
dir_pb.dp_disk = cur_disk;
}
else
{ /* make disk A = 0, B = 1 (for SELDSK) */
dir_pb.dp_disk--;
}
printf("\nSearching disk %d.",dir_pb.dp_disk);
if(strscn(scb,"?")) /* check if ambiguous name */
{
printf("\nError - UNERASE can only revive a single file at a time.");
exit();
}
/* set up a special search control block that will match with
all existing files. */
ssetscb(scba,"*.*",'?',12); /* set file name and initialize scb */
if (argc == 2) /* no user number specified */
user = bdos(GETUSER,0xFF); /* get current user number */
else
{
user = atoi(argv[2]); /* get specified number */
if (user > 15)
{
printf("\nUser number can only be between 0 - 15.");
exit();
}
}
/* build a bit vector that shows the allocation blocks
currently in use. scba has been set up to match all
active directory entries on the disk. */
build_bv(inuse_bv,scba);
/* build a bit vector for the file to be restored showing
which allocation blocks will be needed for the file. */
if (!build_bv(file_bv,scb))
{
printf("\nNo directory entries found for file %s.",
argv[1]);
exit();
}
/* perform a boolean AND of the two bit vectors. */
bv_and(file_bv,inuse_bv,file_bv);
/* check if the result is non-zero - if so, then one or more
of the allocation blocks required by the erased file is
already in use for an existing file and the file cannot
be restored. */
if (bv_nz(file_bv))
{
printf("\n--- This file cannot be restored as some parts of it");
printf("\n have been re-used for other files! ---");
exit();
}
/* continue on to restore the file by changing all the entries
in the directory to have the specified user number.
Note : the problem is complicated by the fact that there
may be several entries in the directory that are for the
same file name and type, and even have the same extent
number. For this reason, a bit map is kept of the extent
numbers unerased - duplicate extent numbers will not be
unerased. */
/* set up the bit vector for up to 127 unerased extents */
bv_make(extents,16); /* 16 * 8 bits */
/* set the directory to "closed", and force the get_nde
function to open it. */
dir_pb.dp_open = 0;
/* while not at the end of the directory, return a pointer to
the next entry in the directory. */
while(dir_entry = get_nde(dir_pb))
{
/* check if user = 0xE5 and name, type match */
if (comp_fname(scb,dir_entry) == NAME_EQ)
{
/* test if this extent has already been
unerased */
if (bv_test(extents,dir_entry -> de_extent))
{ /* yes it has */
printf("\n\t\tExtent #%d of %s ignored.",
dir_entry -> de_extent,argv[1]);
continue; /* do not unerase this one */
}
else /* indicate this extent unerased */
{
bv_set(extents,dir_entry -> de_extent);
dir_entry -> de_userno = user; /* unerase entry */
dir_pb.dp_write = 1; /* need to write sector back */
printf("\n\tExtent #%d of %s unerased.",
dir_entry -> de_extent,argv[1]);
}
}
}
printf("\n\nFile %s unerased in User Number %d.",
argv[1],user);
bdos(SETDISK,cur_disk); /* reset to current disk */
}
build_bv(bv,scb) /* build bit vector (from directory) */
/* This function scans the directory of the disk specified in
the directory parameter block (declared as a global variable),
and builds the specified bit vector showing all the allocation
blocks used by files matching the name in the search control
block. */
/* Entry Parameters */
struct _bv *bv; /* pointer to the bit vector */
struct _scb *scb; /* pointer to search control block */
/* also uses : directory parameter block (dir_pb) */
/* Exit Parameters
The specified bit vector will be created, and will have 1-bits
set wherever an allocation block is found in a directory
entry that matches the search control block.
It also returns the number of directory entries matched. */
{
unsigned abno; /* allocation block number */
struct _dpb *dpb; /* pointer to the disk parameter block in the BIOS */
int mcount; /* match count of dir. entries matched */
mcount = 0; /* initialize match count */
dpb = get_dpb(dir_pb.dp_disk); /* get disk parameter block address */
/* make the bit vector with one byte for each 8 allocation
blocks + 1 */
if (!(bv_make(bv,(dpb -> dpb_maxabn >>3)+1)))
{
printf("\nError - Insufficient memory to make a bit vector.");
exit();
}
/* set directory to "closed" to force the get_nde
function to open it. */
dir_pb.dp_open = 0;
/* now scan the directory building the bit vector */
while(dir_entry = get_nde(dir_pb))
{
/* compare user number (which can legitimately be
0xE5), the file name and the type). */
if (comp_fname(scb,dir_entry) == NAME_EQ)
{
++mcount; /* update match count */
for (count = 0; /* start with the first alloc. block */
count < dir_pb.dp_nabpde; /* for number of alloc. blks per dir. entry */
count++)
{
/* set the appropriate bit number for
each non-zero allocation block number */
if (dir_pb.dp_nabpde == 8) /* assume 8 2-byte numbers */
{
abno = dir_entry -> _dirab.de_long[count];
}
else /* assume 16 1-byte numbers */
{
abno = dir_entry -> _dirab.de_short[count];
}
if (abno) bv_set(bv,abno); /* set the bit */
}
}
}
return mcount; /* return number of dir entries matched */
}
chk_use(argc) /* check usage */
/* This function checks that the correct number of
parameters has been specified, outputting instructions
if not. */
/* Entry Parameter */
int argc; /* Count of the number of arguments on the command line */
{
/* The minimum value of argc is 1 (for the program name itself),
so argc is always one greater than the number of parameters
on the command line */
if (argc == 1 || argc > 3)
{
printf("\nUsage :");
printf("\n\tUNERASE {d:}filename.typ {user}");
printf("\n\tOnly a single unambiguous file name can be used.)");
exit();
}
} /* end chk_use */
ssetscb(scb,fname,user,length) /* special version of set search control block */
/* This function sets up a search control block according
to the file name, type, user number and number of bytes to
compare.
The file name can take the following forms :
filename
filename.typ
d:filename.typ
It sets the bit map according to which disks should be searched.
For each selected disk, it checks to see if an error is generated
when selecting the disk (i.e. if there are disk tables in the BIOS
for the disk). */
/* Entry Parameters */
struct _scb *scb; /* pointer to search control block */
char *fname; /* pointer to the file name */
short user; /* user number to be matched */
int length; /* number of bytes to compare */
/* Exit Parameters
Disk number to be searched. (A = 1, B = 2...)
*/
{
short disk_id; /* disk number to search */
setfcb(scb,fname); /* set search control block as though it
is a file control block. */
disk_id = scb -> scb_userno; /* set disk_id before it gets overwritten
by the user number */
scb -> scb_userno = user; /* set user number */
scb -> scb_length = length; /* set number of bytes to compare */
return disk_id;
} /* end setscb */


View File

@@ -0,0 +1,786 @@
title 'console command processor (CCP), ver 2.0'
; assembly language version of the CP/M console command processor
;
; version 1.4
;
; Copyright (c) 1976, 1977, 1978
; Digital Research
; Box 579, Pacific Grove,
; California, 93950
;
false equ 0000h
true equ not false
testing equ false ;true if debugging
;
;
if testing
org 3400h
bdosl equ $+800h ;bdos location
else
org 000h
bdosl equ $+800h ;bdos location
endif
tran equ 100h
tranm equ $
ccploc equ $
;
; ********************************************************
; * Base of CCP contains the following code/data *
; * ccp: jmp ccpstart (start with command) *
; * jmp ccpclear (start, clear command) *
; * ccp+6 127 (max command length) *
; * ccp+7 comlen (command length = 00) *
; * ccp+8 ' ... ' (16 blanks) *
; ********************************************************
; * Normal entry is at ccp, where the command line given *
; * at ccp+8 is executed automatically (normally a null *
; * command with comlen = 00). An initializing program *
; * can be automatically loaded by storing the command *
; * at ccp+8, with the command length at ccp+7. In this *
; * case, the ccp executes the command before prompting *
; * the console for input. Note that the command is exe-*
; * cuted on both warm and cold starts. When the command*
; * line is initialized, a jump to "jmp ccpclear" dis- *
; * ables the automatic command execution. *
; ********************************************************
;
jmp ccpstart ;start ccp with possible initial command
jmp ccpclear ;clear the command buffer
maxlen: db 127 ;max buffer length
comlen: db 0 ;command length (filled in by dos)
; (command executed initially if comlen non zero)
combuf:
db ' ' ;8 character fill
db ' ' ;8 character fill
db 'COPYRIGHT (C) 1978, DIGITAL RESEARCH '; 38
ds 128-($-combuf)
; total buffer length is 128 characters
comaddr:dw combuf ;address of next to char to scan
staddr: ds 2 ;starting address of current fillfcb request
;
diska equ 0004h ;disk address for current disk
bdos equ 0005h ;primary bdos entry point
buff equ 0080h ;default buffer
fcb equ 005ch ;default file control block
;
rcharf equ 1 ;read character function
pcharf equ 2 ;print character function
pbuff equ 9 ;print buffer function
rbuff equ 10 ;read buffer function
breakf equ 11 ;break key function
liftf equ 12 ;lift head function (no operation)
initf equ 13 ;initialize bdos function
self equ 14 ;select disk function
openf equ 15 ;open file function
closef equ 16 ;close file function
searf equ 17 ;search for file function
searnf equ 18 ;search for next file function
delf equ 19 ;delete file function
dreadf equ 20 ;disk read function
dwritf equ 21 ;disk write function
makef equ 22 ;file make function
renf equ 23 ;rename file function
logf equ 24 ;return login vector
cself equ 25 ;return currently selected drive number
dmaf equ 26 ;set dma address
;
; special fcb flags
rofile equ 9 ;read only file
sysfile equ 10 ;system file flag
;
; special characters
cr equ 13 ;carriage return
lf equ 10 ;line feed
la equ 5fh ;left arrow
eofile equ 1ah ;end of file
;
; utility procedures
printchar:
mov e,a! mvi c,pcharf! jmp bdos
;
printbc:
;print character, but save b,c registers
push b! call printchar! pop b! ret
;
crlf:
mvi a,cr! call printchar
mvi a,lf! jmp printchar
;
;
print: ;print string starting at b,c until next 00 entry
push b! call crlf! pop h ;now print the string
prin0: mov a,m! ora a! rz ;stop on 00
inx h! push h ;ready for next
call printchar! pop h ;character printed
jmp prin0 ;for another character
;
initialize:
mvi c,initf! jmp bdos
;
select:
mov e,a! mvi c,self! jmp bdos
;
;
open: ;open the file given by d,e
mvi c, openf! call bdos! sta dcnt! inr a! ret
;
openc: ;open comfcb
xra a! sta comrec ;clear next record to read
lxi d,comfcb! jmp open
;
close: ;close the file given by d,e
mvi c,closef! call bdos! sta dcnt! inr a! ret
;
search: ;search for the file given by d,e
mvi c,searf! call bdos! sta dcnt! inr a! ret
;
searchn:
;search for the next occurrence of the file given by d,e
mvi c,searnf! call bdos! sta dcnt! inr a! ret
;
searchcom:
;search for comfcb file
lxi d,comfcb! jmp search
;
delete: ;delete the file given by d,e
mvi c,delf! jmp bdos
;
;
diskread:
;read the next record from the file given by d,e
mvi c,dreadf! call bdos! ora a! ret
;
diskreadc:
;read the comfcb file
lxi d,comfcb! jmp diskread
;
diskwrite:
;write the next record to the file given by d,e
mvi c,dwritf! call bdos! ora a! ret
;
make: ;create the file given by d,e
mvi c,makef! call bdos! sta dcnt! ret
;
renam: ;rename the file given by d,e
mvi c,renf! jmp bdos
;
;
translate:
;translate character in register A to upper case
cpi 61h! rc ;return if below lower case a
cpi 7bh! rnc ;return if above lower case z
ani 5fh! ret ;translated to upper case
;
readcom:
;read the next command into the command buffer
;check for submit file
lda submit! ora a! jz nosub
;scanning a submit file
;change drives to open and read the file
lda cdisk! ora a! mvi a,0! cnz select
;have to open again in case xsub present
lda subrc! dcr a ;read last record(s) first
sta subcr ;current record to read
lxi d,subfcb! call diskread ;end of file if last record
jnz nosub
;disk read is ok, transfer to combuf
lxi d,comlen! lxi h,buff! mvi b,128! call move0
;line is transferred, close the file with a
;deleted record
lxi h,subrc ; mvi m,0 ;clear fwflag
dcr m ;one less record
lxi d,subfcb! call close! jz nosub
;close went ok, return to original drive
lda cdisk! ora a! cnz select
;print to the 00
lxi h,combuf! call prin0
call break$key! jz noread
call del$sub! jmp ccp ;break key depressed
;
nosub: ;no submit file! call del$sub
;translate to upper case, store zero at end
mvi c,rbuff! lxi d,maxlen! call bdos
noread: ;enter here from submit file
;set the last character to zero for later scans
lxi h,comlen! mov b,m ;length is in b
readcom0: inx h! mov a,b! ora a ;end of scan?
jz readcom1! mov a,m ;get character and translate
call translate! mov m,a! dcr b! jmp readcom0
;
readcom1: ;end of scan, h,l address end of command
mov m,a ;store a zero
lxi h,combuf! shld comaddr ;ready to scan to zero
ret
;
break$key:
;check for a character ready at the console
mvi c,breakf! call bdos
ora a! rz
mvi c,rcharf! call bdos ;character cleared
ora a! ret
;
clifter:
mvi c,liftf! jmp bdos
;
cselect:
;get the currently selected drive number to reg-A
mvi c,cself! jmp bdos
;
cpyblock:
ldax d! mov m,a! inx d! inx h
dcr c! jnz cpyblock! ret
;
setdma:
;set dma address to d,e
mvi c,dmaf! jmp bdos
;
del$sub:
;delete the submit file, and set submit flag to false
lxi h,submit! mov a,m! ora a! rz ;return if no sub file
mvi m,0 ;submit flag is set to false
xra a! call select ;on drive a to erase file
lxi d,subfcb! call delete
lda cdisk! jmp select ;back to original drive
;
serialize:
;check serialization
lxi d,serial! lxi h,bdosl! mvi b,6 ;check six bytes
ser0: ldax d! cmp m! jnz badserial
inx d! inx h! dcr b! jnz ser0
ret ;serial number is ok
;
comerr:
;error in command string starting at position
;'staddr' and ending with first delimiter
call crlf ;space to next line
lhld staddr ;h,l address first to print
comerr0: ;print characters until blank or zero
mov a,m! cpi ' '! jz comerr1; not blank
ora a! jz comerr1; not zero, so print it
push h! call printchar! pop h! inx h
jmp comerr0; for another character
comerr1: ;print question mark,and delete sub file
mvi a,'?'! call printchar
call crlf! call del$sub
jmp ccp ;restart with next command
;
; fcb scan and fill subroutine (entry is at fillfcb below)
;fill the comfcb, indexed by A (0 or 16)
;subroutines
delim: ;look for a delimiter
ldax d! ora a! rz ;not the last element
cpi ' '! jc comerr ;non graphic
rz ;treat blank as delimiter
cpi '='! rz
cpi la! rz ;left arrow
cpi '.'! rz
cpi ':'! rz
cpi ';'! rz
cpi '<'! rz
cpi '>'! rz
ret ;delimiter not found
;
deblank: ;deblank the input line
ldax d! ora a! rz ;treat end of line as blank
cpi ' '! rnz! inx d! jmp deblank
;
addh: ;add a to h,l
add l! mov l,a! rnc
inr h! ret
;
fillfcb0:
;equivalent to fillfcb(0)
mvi a,0
;
fillfcb:
lxi h,comfcb! call addh! push h! push h ;fcb rescanned at end
xra a! sta sdisk ;clear selected disk (in case A:...)
lhld comaddr! xchg ;command address in d,e
call deblank ;to first non-blank character
xchg! shld staddr ;in case of errors
xchg! pop h ;d,e has command, h,l has fcb address
;look for preceding file name A: B: ...
ldax d! ora a! jz setcur0 ;use current disk if empty command
sbi 'A'-1! mov b,a ;disk name held in b if : follows
inx d! ldax d! cpi ':'! jz setdsk ;set disk name if :
;
setcur: ;set current disk
dcx d ;back to first character of command
setcur0:
lda cdisk! mov m,a! jmp setname
;
setdsk: ;set disk to name in register b
mov a,b! sta sdisk ;mark as disk selected
mov m,b! inx d ;past the :
;
setname: ;set the file name field
mvi b,8 ;file name length (max)
setnam0: call delim! jz padname ;not a delimiter
inx h! cpi '*'! jnz setnam1 ;must be ?'s
mvi m,'?'! jmp setnam2 ;to dec count
;
setnam1: mov m,a ;store character to fcb! inx d
setnam2: dcr b ;count down length! jnz setnam0
;
;end of name, truncate remainder
trname: call delim! jz setty ;set type field if delimiter
inx d! jmp trname
;
padname: inx h! mvi m,' '! dcr b! jnz padname
;
setty: ;set the type field
mvi b,3! cpi '.'! jnz padty ;skip the type field if no .
inx d ;past the ., to the file type field
setty0: ;set the field from the command buffer
call delim! jz padty! inx h! cpi '*'! jnz setty1
mvi m,'?' ;since * specified! jmp setty2
;
setty1: ;not a *, so copy to type field
mov m,a! inx d
setty2: ;decrement count and go again
dcr b! jnz setty0
;
;end of type field, truncate
trtyp: ;truncate type field
call delim! jz efill! inx d! jmp trtyp
;
padty: ;pad the type field with blanks
inx h! mvi m,' '! dcr b! jnz padty
;
efill: ;end of the filename/filetype fill, save command address
;fill the remaining fields for the fcb
mvi b,3
efill0: inx h! mvi m,0! dcr b! jnz efill0
xchg! shld comaddr ;set new starting point
;
;recover the start address of the fcb and count ?'s
pop h! lxi b,11 ;b=0, c=8+3
scnq: inx h! mov a,m! cpi '?'! jnz scnq0
;? found, count it in b! inr b
scnq0: dcr c! jnz scnq
;
;number of ?'s in c, move to a and return with flags set
mov a,b! ora a! ret
;
intvec:
;intrinsic function names (all are four characters)
db 'DIR '
db 'ERA '
db 'TYPE'
db 'SAVE'
db 'REN '
intlen equ ($-intvec)/4 ;intrinsic function length
serial: ;db 0,0,0,0,0,0
db 14h,0eh,0,0,7h,06dh
;
;
intrinsic:
;look for intrinsic functions (comfcb has been filled)
lxi h,intvec! mvi c,0 ;c counts intrinsics as scanned
intrin0: mov a,c! cpi intlen ;done with scan?! rnc
;no, more to scan
lxi d,comfcb+1 ;beginning of name
mvi b,4 ;length of match is in b
intrin1: ldax d! cmp m ;match?
jnz intrin2 ;skip if no match
inx d! inx h! dcr b
jnz intrin1 ;loop while matching
;
;complete match on name, check for blank in fcb
ldax d! cpi ' '! jnz intrin3 ;otherwise matched
mov a,c! ret ;with intrinsic number in a
;
intrin2: ;mismatch, move to end of intrinsic
inx h! dcr b! jnz intrin2
;
intrin3: ;try next intrinsic
inr c ;to next intrinsic number
jmp intrin0 ;for another round
intrin4:
ora a! jnz intrin5! lxi d,subfcb! call open
jz intrin5
mvi a, 0ffh! jmp intrin6
intrin5:
xra a
intrin6:
sta submit
ret
;
ccpclear:
;clear the command buffer
xra a
sta comlen
;drop through to start ccp
ccpstart:
;enter here from boot loader
lxi sp,stack! push b ;save initial disk number
;(high order 4bits=user code, low 4bits=disk#)
push b! call initialize! pop b
push psw! mov a,c! call select
pop psw! pop b
inr a! ora c
call intrin4 ;proper disk is selected, now check sub files
;check for initial command
lda comlen! ora a! jnz ccp0 ;assume typed already
;
ccp:
;enter here on each command or error condition
lxi sp,stack
call crlf ;print d> prompt, where d is disk name
call cselect ;get current disk number
adi 'A'! call printchar
mvi a,'>'! call printchar
call readcom ;command buffer filled
ccp0: ;(enter here from initialization with command full)
lxi d,buff! call setdma ;default dma address at buff
call cselect! sta cdisk ;current disk number saved
call fillfcb0 ;command fcb filled
cnz comerr ;the name cannot be an ambiguous reference
lda sdisk! ora a! jnz userfunc
;check for an intrinsic function
call intrinsic
lxi h,jmptab ;index is in the accumulator
mov e,a! mvi d,0! dad d! dad d ;index in d,e
mov a,m! inx h! mov h,m! mov l,a! pchl
;pc changes to the proper intrinsic or user function
jmptab:
dw direct ;directory search
dw erase ;file erase
dw type ;type file
dw save ;save memory image
dw rename ;file rename
dw userfunc;user-defined function
badserial:
lxi h,di or (hlt shl 8)
shld ccploc! lxi h,ccploc! pchl
;
;
;utility subroutines for intrinsic handlers
readerr:
;print the read error message
lxi b,rdmsg! jmp print
rdmsg: db 'READ ERROR',0
;
nofile:
;print no file message
lxi b,nofmsg! jmp print
nofmsg: db 'NOT FOUND',0
;
getnumber: ;read a number from the command line
;convert the byte value in comfcb to binary
lxi h,comfcb+1! lxi b,11 ;(b=0, c=11)
;value accumulated in b, c counts name length to zero
conv0: mov a,m! cpi ' '! jz conv1
;more to scan, convert char to binary and add
inx h! sui '0'! cpi 10! jnc comerr ;valid?
mov d,a ;save value! mov a,b ;mult by 10
ani 1110$0000b! jnz comerr
mov a,b ;recover value
rlc! rlc! rlc ;*8
add b! jc comerr
add b! jc comerr ;*8+*2 = *10
add d! jc comerr ;+digit
mov b,a! dcr c! jnz conv0 ;for another digit
ret
conv1: ;end of digits, check for all blanks
mov a,m! cpi ' '! jnz comerr ;blanks?
inx h! dcr c! jnz conv1
mov a,b ;recover value! ret
;
movename:
;move 3 characters from h,l to d,e addresses
mvi b,3
move0: mov a,m! stax d! inx h! inx d
dcr b! jnz move0
ret
;
addhcf: ;buff + a + c to h,l followed by fetch
lxi h,buff! add c! call addh! mov a,m! ret
;
setdisk:
;change disks for this command, if requested
xra a! sta comfcb ;clear disk name from fcb
setdisk1:
lda sdisk! ora a! rz ;no action if not specified
dcr a! lxi h,cdisk! cmp m! rz ;already selected
jmp select
;
resetdisk:
;return to original disk after command
lda sdisk! ora a! rz ;no action if not selected
dcr a! lxi h,cdisk! cmp m! rz ;same disk
lda cdisk! jmp select
;
;individual intrinsics follow
direct:
;directory search
call fillfcb0 ;comfcb gets file name
call setdisk ;change disk drives if requested
lxi h,comfcb+1! mov a,m ;may be empty request
cpi ' '! jnz dir1 ;skip fill of ??? if not blank
;set comfcb to all ??? for current disk
mvi b,11 ;length of fill ????????.???
dir0: mvi m,'?'! inx h! dcr b! jnz dir0
;not a blank request, must be in comfcb
dir1:
call searchcom ;first one has been found
cz nofile ;not found message
dir2: jz endir
;found, but may be system file
call crlf! call cselect
;current disk in A
adi 'A'! call printchar
mvi a,':'! call printchar
mvi a,' '! call printchar
dirhdr1:
lda dcnt! rrc! rrc! rrc! ani 60h! mov c,a
;compute position of name in buffer
mvi b,1 ;start with first character of name
dir3: mov a,b! call addhcf ;buff+a+c fetched
cpi ' '! jnz dir4 ;check for blank type
mvi a,9! call addhcf ;first char of type
mvi a,' '! cmp m! jz dir5
dir4:
call printbc ;char printed
inr b! mov a,b! cpi 12! jnc dir5
;check for break between names
cpi 9! jnz dir3 ;for another char
;print a blank between names
mvi a,' '! call printbc! jmp dir3
;
dir5: ;end of current entry
dir6: call break$key ;check for interrupt at keyboard
jnz endir ;abort directory search
call searchn! jmp dir2 ;for another entry
endir: ;end of directory scan
jmp retcom
;
;
erase:
call fillfcb0 ;cannot be all ???'s
cpi 11
jnz getout
;erasing all of the disk
lxi b,ermsg! call print!
call readcom
lxi h,comlen! dcr m! jnz ccp ;bad input
inx h! mov a,m! cpi 'Y'! jnz ccp
;ok, erase the entire diskette
inx h! shld comaddr ;otherwise error at retcom
erasefile:
lxi h, comfcb! mvi m,'?'
call setdisk1
lxi d,comfcb! call delete
call initialize! jmp retcom
getout:
call setdisk
lxi d, comfcb! call delete
jmp retcom
;
ermsg: db 'ALL FILES (Y/N)?',0
;
type:
call fillfcb0! jnz comerr ;don't allow ?'s in file name
call setdisk! call openc ;open the file
jz typerr ;zero flag indicates not found
;file opened, read 'til eof
call crlf! lxi h,bptr! mvi m,255 ;read first buffer
type0: ;loop on bptr
lxi h,bptr! mov a,m! cpi 128 ;end buffer
jc type1! push h ;carry if 0,1,...,127
;read another buffer full
call diskreadc! pop h ;recover address of bptr
jnz typeof ;hard end of file
xra a! mov m,a ;bptr = 0
type1: ;read character at bptr and print
inr m ;bptr = bptr + 1
lxi h,buff! call addh ;h,l addresses char
mov a,m! cpi eofile! jz retcom
call printchar
call break$key! jnz retcom ;abort if break
jmp type0 ;for another character
;
typeof: ;end of file, check for errors
dcr a! jz retcom
call readerr
typerr: call resetdisk! jmp comerr
;
save:
call fillfcb0! lda sdisk! ora a! jnz comerr
call getnumber; value to register a
push psw ;save it for later
;
;should be followed by a file to save the memory image
call fillfcb0
jnz comerr ;cannot be ambiguous
call setdisk ;may be a disk change
lxi d,comfcb! push d! call delete ;existing file removed
pop d! call make ;create a new file on disk
call openc! jz save1
pop psw ;#pages to write is in a, change to #sectors
mov l,a! mvi h,0! dad h!
lxi d,tran ;h,l is sector count, d,e is load address
save0: ;check for sector count zero
mov a,h! ora l! jz save2 ;may be completed
dcx h ;sector count = sector count - 1
push h ;save it for next time around
lxi h,128! dad d! push h ;next dma address saved
call setdma ;current dma address set
lxi d,comfcb! call diskwrite
pop d! pop h ;dma address, sector count
jnz save1 ;may be disk full case
jmp save0 ;for another sector
;
save1: ;end of dump, close the file
lxi b, fullmsg! call print
save2:
lxi d,comfcb! call close
jnz retsave ;for another command
saverr: ;must be full or read only disk
lxi b,closemsg! call print
retsave:
jmp retcom
fullmsg: db 'NO SPACE',0
;
closemsg: db 'CANNOT CLOSE',0
;
rename:
;rename a file on a specific disk
call fillfcb0! jnz comerr ;must be unambiguous
lda sdisk! push psw ;save for later compare
call setdisk ;disk selected
call searchcom ;is new name already there?
jnz renerr3
;file doesn't exist, move to second half of fcb
lxi h,comfcb! lxi d,comfcb+16! mvi b,16! call move0
;check for = or left arrow
lhld comaddr! xchg! call deblank
cpi '='! jz ren1 ;ok if =
cpi la! jnz renerr2
ren1: xchg! inx h! shld comaddr ;past delimiter
;proper delimiter found
call fillfcb0! jnz renerr2
;check for drive conflict
pop psw! mov b,a ;previous drive number
lxi h,sdisk! mov a,m! ora a! jz ren2
;drive name was specified. same one?
cmp b! mov m,b! jnz renerr2
ren2: mov m,b ;store the name in case drives switched
xra a! sta comfcb! call searchcom ;is old file there?
jz renerr1
;
;everything is ok, rename the file
lxi d,comfcb! call renam
jmp retcom
;
renerr1:; no file on disk
call nofile! jmp retcom
renerr2:; ambigous reference/name conflict
call resetdisk! jmp comerr
renerr3:; file already exists
lxi b,renmsg! call print! jmp retcom
renmsg: db 'FILE EXISTS',0
;
;
userfunc:
call serialize ;check serialization
;load user function and set up for execution
lda comfcb+1! cpi ' '! jnz user0
;no file name, but may be disk switch
lda sdisk! ora a! jz endcom ;no disk name if 0
dcr a! sta cdisk! ;call setdiska ;set user/disk
sta diska
call select! jmp endcom
user0: ;file name is present
lxi d,comfcb+9! ldax d! cpi ' '! jnz comerr ;type ' '
push d! call setdisk! pop d! lxi h,comtype ;.com
call movename ;file type is set to .com
call openc! jz userer
;file opened properly, read it into memory
lxi h,tran ;transient program base
load0: push h ;save dma address
xchg! call setdma
lxi d,comfcb! call diskread! jnz load1
;sector loaded, set new dma address and compare
pop h! lxi d,128! dad d
lxi d,tranm ;has the load overflowed?
mov a,l! sub e! mov a,h! sbb d! jnc loaderr
jmp load0 ;for another sector
;
load1: pop h! dcr a! jnz loaderr ;end file is 1
call resetdisk ;back to original disk
call fillfcb0! lxi h,sdisk! push h
mov a,m! sta comfcb ;drive number set
mvi a,16! call fillfcb ;move entire fcb to memory
pop h! mov a,m! sta comfcb+16
xra a! sta comrec ;record number set to zero
lxi d,fcb! lxi h,comfcb! mvi b,33! call move0
;move command line to buff
lxi h,combuf
bmove0: mov a,m! ora a! jz bmove1! cpi ' '! jz bmove1
inx h! jmp bmove0 ;for another scan
;first blank position found
bmove1: mvi b,0! lxi d,buff+1! ;ready for the move
bmove2: mov a,m! stax d! ora a! jz bmove3
;more to move
inr b! inx h! inx d! jmp bmove2
bmove3: ;b has character count
mov a,b! sta buff
call crlf
;now go to the loaded program
lda cdisk! sta diska! call clifter! lxi d, buff
call setdma ;user code saved
;low memory diska contains user code
call tran ;gone to the loaded program
lxi sp,stack ;may come back here
lda cdisk! call select
jmp ccp
;
userer: ;arrive here on command error
call resetdisk! jmp comerr
;
loaderr:;cannot load the program
lxi b,loadmsg! call print
jmp retcom
loadmsg: db 'LOAD ERROR',0
comtype: db 'COM' ;for com files
;
;
retcom: ;reset disk before end of command check
call resetdisk
;
endcom: ;end of intrinsic command
call fillfcb0 ;to check for garbage at end of line
lda comfcb+1! sui ' '! lxi h,sdisk! ora m
;0 in accumulator if no disk selected, and blank fcb
jnz comerr
jmp ccp
;
;
;
; data areas
ds 16 ;8 level stack
stack:
;
; 'submit' file control block
submit: db 0 ;00 if no submit file, ff if submitting
subfcb: db 0,'$$$ ' ;file name is $$$
db 'SUB',0,0 ;file type is sub
submod: db 0 ;module number
subrc: ds 1 ;record count filed
ds 16 ;disk map
subcr: ds 1 ;current record to read
;
; command file control block
comfcb: ds 32 ;fields filled in later
comrec: ds 1 ;current record to read/write
dcnt: ds 1 ;disk directory count (used for error codes)
cdisk: ds 1 ;current disk
sdisk: ds 1 ;selected disk for current operation
;none=0, a=1, b=2 ...
bptr: ds 1 ;buffer pointer
ds 0eh
ds 38
end ccploc

Binary file not shown.

View File

@@ -0,0 +1 @@
Make CPM 1.4 v 0.1: by Ernie Price. Make CPM 1.4 is a howto on reconstucting the CPM Version 1.4 CCP and BDOS from recovered and regenerated sources.

View File

@@ -0,0 +1,5 @@
rcx
800
nccp.bin
wa00
q

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,253 @@
/* #include <conio.h> */
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct fcb // also directory entry
{
unsigned char user; // not used in CP/M 1.4
unsigned char name[8]; // file name
unsigned char type[3]; // file extension
unsigned char ext; // extent number
unsigned char notused[2];
unsigned char rc; // record count in this extent
unsigned char dm[16]; // single byte extent numbers
};
#define DIR_ENT_SZ 32 // size of one directory entry
#define SECT_SZ 128 // disk sector size
#define SPB 8 // sectors per block
#define DIR_BLOCKS 2 // 2 blocks reserved for directory
#define SPT 26 // sectors per track
#define RESERVED_TRACKS 2 // for cold boot, ccp and cpm
#define DIR_SECTORS (SPB * DIR_BLOCKS) // sectors for directory
#define DIR_LOCN (SPT * RESERVED_TRACKS) // offset of directory
#define DIR_ENT_PER_SEC (SECT_SZ / DIR_ENT_SZ) // directory entries per sector
unsigned char buff1[SPT * SECT_SZ]; // for de-interleaving
unsigned char buff2[SPT * SECT_SZ]; // "
unsigned char fsec [SECT_SZ];
char fname [20]; // file name for current extent
char fname2[20]; // currently open output file name, if any
FILE *in, // raw image file
*out; // extracted CP/M file
unsigned char exm[1024]; // map of processed blocks
// sector interleave table
unsigned char sectran[] = {
"\x01\x07\x0d\x13\x19\x05\x0b\x11"
"\x17\x03\x09\x0f\x15\x02\x08\x0e"
"\x14\x1a\x06\x0c\x12\x18\x04\x0a"
"\x10\x16\x00\x00\x00\x00\x00\x00"};
char temp[] = "junkimg.jnk"; // name of de-interleaved raw image file
/**************************************************
Read the input image file in gulps of 26 sectors, reorder using the
interleace table, and save the to the de-interleaved file
*/
void RemoveInterleave
(
char *fn1, // ram image file name
char * fn2, // reordered image file name
int flag // not used
)
{
int x;
in = fopen(fn1, "rb");
out = fopen(fn2, "wb");
while (fread(buff1, 1, sizeof(buff1), in)) // read a 'track'
{
for (x = 0; x < SPT; x++) // shuffle
{
memcpy(&buff2[x * SECT_SZ], &buff1[(sectran[x] - 1) * SECT_SZ], SECT_SZ);
}
fwrite(buff2, 1, sizeof(buff2), out);
}
fcloseall();
}
/**************************************************
Copy a file name or extension until a space character is found
or limit is reached.
*/
void copyuntil
(
char *dst, // destination for copy
char *src, // source for copy
int count // max copy length
)
{
while (*src != ' ' && count--)
{
*dst++ = *src++;
}
*dst = 0;
}
/**************************************************
Convert an 8,3 filename into name.ext format
*/
void MakeFilename
(
char *fname, // destination for file name
struct fcb *tfcb // pointer to fcb (directory entry)
)
{
copyuntil(fname, tfcb->name, 8); // copy name part
if (tfcb->type[0] != ' ') // if extension is non-blank
{
strcat(fname, "."); // add a dot
copyuntil(fname + strlen(fname), tfcb->type, 3);
}
}
/**************************************************
Read a logical CP/M sector from the image file. Sector is a logical
number based at the directory root. The reserved sectors(tracks)
are added.
*/
void ReadSector
(
unsigned char *where, // destination for read
int sectorno // logical sector number
)
{
fseek(in, (long)(DIR_LOCN + sectorno) * (long)SECT_SZ, SEEK_SET);
fread(where, 1, SECT_SZ, in);
}
/**************************************************
Construct the file name and copy all records in the specified extent into the
output file. If the file name differs from the previously processed file name
close the previous file and open a new one with the current name.
Since extents are psorecces in sorted order, including the extent number, this
results in recovered files.
*/
void CopyExtentToFile
(
struct fcb *tfcb, // pointer to fcb (directory entry)
int deleted // flag indicating a deleted extent
)
{
int x, // general counter
thisSect, // computed logical sector number
said; // flag for printing
static int extent; // logical extent number
MakeFilename(fname, tfcb); // extract filename
if (strcmp(fname2, fname)) // is it different from before?
{
if (out != NULL)
{
fclose(out);
}
out = fopen(fname, "wb"); // create new output file
strcpy(fname2, fname);
printf("Extracting %sfile %s\n", deleted ? "deleted " : "", fname);
extent = -1;
}
extent++;
//printf("%15s", fname);
for (x = 0; x < tfcb->rc; x++) // fetch record count sectors and save
{
if (!(x % SPB))
{
//printf(" %02x", tfcb->dm[x / SPB]);
}
if ((exm[tfcb->dm[x / SPB]] != 0) && !said)
{
said = 1;
printf(" extent %d has blocks used elsewhere\n");
}
exm[tfcb->dm[x / SPB]] = 1; // mark in block map
// convert logical sector to physical
thisSect = (tfcb->dm[x / SPB] * SPB) + (x % SPB);
ReadSector(fsec, thisSect); // load the file sector
fwrite(fsec, 1, SECT_SZ, out); // store in new file
}
//printf("\n");
}
/**************************************************
Compare routine for qsort of directory
*/
int compare
(
void *a, // pointer to key field 'a'
void *b // pointer to key field 'b'
)
{
return memcmp(a, b, 16); // compare keys
}
/**************************************************
*/
main
(
int argc, // count of command line arguments
char **argv // pointers to arguments
)
{
int x;
struct fcb *tfcb = (struct fcb *)buff1;
//printf("Dir locn %x\n", DIR_LOCN * SECT_SZ);
if (argc < 2)
{
printf("File name required for compare\n\n");
printf("Correct usage is:\n");
printf(" extract <filename>\n\n");
printf(" where <filename> is a diskette image file\n");
exit(1);
}
RemoveInterleave(argv[1], temp, 0);
in = fopen(temp, "rb");
// load the directory into memory
for (x = 0; x < DIR_SECTORS; x++)
{
ReadSector(buff1 + (x * SECT_SZ), x);
}
// sort the directory in [user], name and extent order
qsort(buff1, DIR_SECTORS, DIR_ENT_SZ, compare);
// process the sorted list
for (x = 0; x < (DIR_SECTORS * DIR_ENT_PER_SEC); x++)
{
if ((tfcb->name[0] != 0xe5) && (tfcb->name != 0))
{
CopyExtentToFile(tfcb, tfcb->user == 0xe5);
}
tfcb++;
}
fcloseall();
remove(temp);
}

View File

@@ -0,0 +1,3 @@
@echo off
rem extract the BDOS image from cpm.com
call ..\tools\getcpm ..\binaries\cpm.com bdos.bin 1200 d00

View File

@@ -0,0 +1,4 @@
@echo off
rem extract the CCP image from cpm.com
call ..\tools\getcpm ..\binaries\cpm.com ccp.bin a00 800

View File

@@ -0,0 +1,10 @@
@echo off
rem #1 = inputfn, #2 outputfn #3 = addr, #4 = lth
rem build a script for debug
echo rcx>dbg.bat
echo %4>>dbg.bat
echo n%2>>dbg.bat
echo w%3>>dbg.bat
echo q>>dbg.bat
call debug %1 <dbg.bat

View File

@@ -0,0 +1,176 @@
/*
Intel hex to binary converter Converts the specified file to binary
*/
#include <stdio.h>
#include <conio.h>
#include <assert.h>
typedef struct image_t
{
unsigned lowaddress, highaddress;
} image_t;
//void LoadHexFile(char *fn, unsigned char *where, image_t *im);
#define LINE_SIZE 500 // max size of input hex line
#define MAX_IMG_SZ 0x4000
// Intel hex-file record types
#define RT_DATA 0
#define RT_EOF 1
static int hexsum;
static unsigned char *lyne;
/******************************************************************************
Get a hexadecimal nybble from the input stream. Increment the pointer.
*/
static int getnib(char **bt)
{
int x = **bt - '0';
if (x > 9)
{
x -= 7;
}
(*bt)++;
return x;
}
/******************************************************************************
Get a single unsigned byte (2 nibbles) from the input stream. Increment the
pointer and add the fetched byte to the line checksum
*/
static unsigned long gethex(char **bt)
{
int x;
x = getnib(bt) << 4;
x |= getnib(bt);
hexsum += x;
return x;
}
/******************************************************************************
Get an unsigned short from the input stream, The pointer and the line checksum
are maintained.
*/
static unsigned long getshort(char **bt)
{
unsigned short ret;
ret = gethex(bt) << 8;
ret |= gethex(bt);
return ret;
}
/******************************************************************************
Load the specified Intel format hex file into memory as a binary
image. Returns the file loaded in 'prog' with the base and limit filled in
as im->lowaddress and im->highaddress respectively.
*/
void LoadHexFile(char *fn, unsigned char *prog, image_t *im, unsigned limit)
{
char *p1; // pointer to hex input text
FILE *ff = fopen(fn, "rb");
int lth; // line length byte
unsigned address, // current line address
offset; // 0 offset of first byte
int rt; // record type
unsigned char kh; // fetched byte from hex file
printf("Converting %s\n", fn);
lyne = (unsigned char*)malloc(LINE_SIZE);
im->lowaddress = 0xffff;
im->highaddress = 0;
while (fgets(p1 = lyne, LINE_SIZE, ff))
{
if (*p1++ != ':') // all good Intel hex file have this
{
break;
}
hexsum = 0;
lth = gethex(&p1); // fetch length of line
address = getshort(&p1); // fetch target address
if ((rt = gethex(&p1)) == RT_EOF) // fetch record type
{
break; // end of file
}
switch(rt)
{
default:
printf("Invalid record type %02x\n", rt);
getch();
break;
case RT_DATA: // data bytes
if (address < im->lowaddress)
{
assert(im->highaddress == 0);
im->lowaddress = address;
offset = address;
}
while (lth--)
{
kh = (unsigned char)gethex(&p1);
if ((address - offset) > limit)
{
printf("Hex file too large\n");
getch();
exit(0);
}
*(prog + address++ - offset) = kh;
}
if (address > im->highaddress)
{
im->highaddress = address;
}
break;
}
kh = (unsigned char)gethex(&p1);
if (hexsum & 0xff)
{
printf("Hex conversion error\n");
getch();
exit(0);
}
}
free(lyne);
fclose(ff);
}
/******************************************************************************
Convert the specified Intel format hex file <argv[1]> into memory as a binary
image. Saves the binary image as <argv[2]>.
*/
void main(int argc, char **argv)
{
image_t img;
FILE *out;
char *where = (char*)malloc(MAX_IMG_SZ);
memset(where, 0, MAX_IMG_SZ);
LoadHexFile(argv[1], where, &img, MAX_IMG_SZ);
out = fopen(argv[2], "wb");
fwrite(where, 1, img.highaddress - img.lowaddress, out);
fclose(out);
}

View File

@@ -0,0 +1,17 @@
set :F0:=..\isis\plm80
set :F1:=..\isis\utils
set :F2:=.
set :F3:=..\isis\asm80
..\isis\utils\isis :F0:plm80 :F2:bdos.plm
..\isis\utils\isis :F3:asm80 :F2:bdosi.src
..\isis\utils\isis :F1:link :F2:bdosi.obj,:F2:bdos.obj,:F1:plm80.lib to :F2:bdos.mod
del bdosi.obj
del bdos.obj
..\isis\utils\isis :F1:locate :F2:bdos.mod code(0800h) stacksize(0)
del bdos.mod
..\isis\utils\isis :F1:objhex :F2:bdos to :F2:bdos.hex
del bdos
..\tools\hexcon bdos.hex bdos.abs
del bdos.hex

View File

@@ -0,0 +1,7 @@
@echo off
set savepath=%path%
set path=..\tools2;%path%
rmac os2ccp
link os2ccp[l0]
set path=%savepath%
set savepath=

View File

@@ -0,0 +1,2 @@
all:
gcc -o exchange -g -fno-builtin -DRLI *c

View File

@@ -0,0 +1,84 @@
Greetings!
This is a utility that allows CP/M disk images to be manipulated under
Unix. It supports disk images in either the standard 8" SSSD format or
the P112 3.5" format. Disk images can be examined and files imported and
exported from them.
The source is a slightly hacked over copy of CP/M-68K which has
been worked over to allow compilation using GCC, to wit:
- A handful of typecasts have been dropped in.
- An assumption that an int is 16 bits has been fixed.
- An assumption that chars are signed has been fixed.
- The file diverge.h renames all of the globals to ensure they
don't collide with operating system globals.
- I changed the way the BDOS communicates with the BIOS; see
biosdef.h
- There are probably a couple of other minor changes I made that
I've forgotten (perhaps renaming a routine or two before I
decided to make diverge.h).
- A BIOS that allows the program to be run under Unix.
I've also added a couple of built-in commands:
- EXIT causes CP/M-68K to exit, returning to the host operating
system.
- IMPORT copies a file from the current directory of the host
operating system into the disk image.
- EXPORT copies a file from the disk image into the current
directory of the host operating system.
IMPORT and EXPORT do not accept wildcards. This is just a quickie hack
that turned out to be useful, not a snazzy disk image manipulation
program.
To build the program, execute make. This results in an executable called
exchange. The command-line syntax is:
exchange [-p112] file.img
-p112 This option tells the file to use the P112 3.5" disk format
for the image. By default, the the standard 8" SSSD format is
used.
file.img is the name of the disk image that should be used.
In this example, I copy the file BDOS.LST from an 8" SSSD image
containing CP/M 1.4 sources (obtained from http://cpm.z80.de/) to user 1
of the P112 boot disk image (obtained from
http://members.iinet.net.au/~daveb/p112/p112.html):
> mac> uname -a
> Linux mac.no.domain 2.4.22-2f #1 Sun Nov 9 16:49:49 EST 2003 ppc ...
> mac> ls
> bdosPLM.img bootdisk.img exchange
> mac> ./exchange bdosPLM.img
>
> A>dir
> A: BDOSI SRC : BDOS PLM : BDOS OBJ : BDOS LST
> A>export BDOS.LST
> export BDOS .LST -> BDOS.LST
> A>exit
> mac> ls
> BDOS.LST bdosPLM.img bootdisk.img exchange
> mac> ./exchange -p112 bootdisk.img
>
> A>user 1
>
> 1A>dir
> A: RECEIVE PAS : UUENCODE PAS : UUDECODE PAS : USER MAC : ROM UUE
> A: ROM PAS : ROM COM
> 1A>import bdos.lst
> import BDOS.LST -> BDOS .LST
> 1A>dir
> A: RECEIVE PAS : UUENCODE PAS : UUDECODE PAS : USER MAC : ROM UUE
> A: ROM PAS : ROM COM : BDOS LST
> 1A>exit
> mac>
Enjoy!
Roger Ivie
anachronda@hotmail.com

View File

@@ -0,0 +1,89 @@
Program by Roger Ivie. Exchange is a utility that allows CP/M disk images to be manipulated under Unix. It supports disk images in either the standard 8" SSSD format or the P112 3.5" format. Disk images can be examined and files imported and exported from them.
The source is a slightly hacked over copy of CP/M-68K BDOS and CCP which has been worked over to allow compilation using GCC.
/README
Greetings!
This is a utility that allows CP/M disk images to be manipulated under
Unix. It supports disk images in either the standard 8" SSSD format or
the P112 3.5" format. Disk images can be examined and files imported and
exported from them.
The source is a slightly hacked over copy of CP/M-68K which has
been worked over to allow compilation using GCC, to wit:
- A handful of typecasts have been dropped in.
- An assumption that an int is 16 bits has been fixed.
- An assumption that chars are signed has been fixed.
- The file diverge.h renames all of the globals to ensure they
don't collide with operating system globals.
- I changed the way the BDOS communicates with the BIOS; see
biosdef.h
- There are probably a couple of other minor changes I made that
I've forgotten (perhaps renaming a routine or two before I
decided to make diverge.h).
- A BIOS that allows the program to be run under Unix.
I've also added a couple of built-in commands:
- EXIT causes CP/M-68K to exit, returning to the host operating
system.
- IMPORT copies a file from the current directory of the host
operating system into the disk image.
- EXPORT copies a file from the disk image into the current
directory of the host operating system.
IMPORT and EXPORT do not accept wildcards. This is just a quickie hack
that turned out to be useful, not a snazzy disk image manipulation
program.
To build the program, execute make. This results in an executable called
exchange. The command-line syntax is:
exchange [-p112] file.img
-p112 This option tells the file to use the P112 3.5" disk format
for the image. By default, the the standard 8" SSSD format is
used.
file.img is the name of the disk image that should be used.
In this example, I copy the file BDOS.LST from an 8" SSSD image
containing CP/M 1.4 sources (obtained from http://cpm.z80.de/) to user 1
of the P112 boot disk image (obtained from
http://members.iinet.net.au/~daveb/p112/p112.html):
> mac> uname -a
> Linux mac.no.domain 2.4.22-2f #1 Sun Nov 9 16:49:49 EST 2003 ppc ...
> mac> ls
> bdosPLM.img bootdisk.img exchange
> mac> ./exchange bdosPLM.img
>
> A>dir
> A: BDOSI SRC : BDOS PLM : BDOS OBJ : BDOS LST
> A>export BDOS.LST
> export BDOS .LST -> BDOS.LST
> A>exit
> mac> ls
> BDOS.LST bdosPLM.img bootdisk.img exchange
> mac> ./exchange -p112 bootdisk.img
>
> A>user 1
>
> 1A>dir
> A: RECEIVE PAS : UUENCODE PAS : UUDECODE PAS : USER MAC : ROM UUE
> A: ROM PAS : ROM COM
> 1A>import bdos.lst
> import BDOS.LST -> BDOS .LST
> 1A>dir
> A: RECEIVE PAS : UUENCODE PAS : UUDECODE PAS : USER MAC : ROM UUE
> A: ROM PAS : ROM COM : BDOS LST
> 1A>exit
> mac>
Enjoy!
Roger Ivie
anachronda@hotmail.com

Some files were not shown because too many files have changed in this diff Show More