home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / enterprs / cpm / utils / a / 28may87r.ark / CXROM1.ASM < prev    next >
Encoding:
Assembly Source File  |  1989-09-27  |  22.3 KB  |  1,211 lines

  1.  
  2.     title    'CP/M 3 ROM loader        13 May 85'
  3.  
  4.  
  5.     maclib    z80
  6.  
  7.     maclib    cpm3
  8.  
  9.     maclib    cxequ
  10.  
  11.     maclib    x6502
  12.  
  13.  
  14. boot$8502    equ    1100h
  15. lines        equ    24        ; number of user lines on screen(s)
  16.  
  17.  
  18. rownum    macro    row,col
  19.     db    row+80h,col
  20.     endm
  21.  
  22.  
  23.  
  24. ;
  25. ;    power on location
  26. ;
  27.     org    00h        ; RST 0
  28.  
  29.     mvi    a,3eh
  30.     sta    force$map
  31.     jmp    power$up        ; continue init somewhere else
  32.  
  33.  
  34. ;
  35. ;    boot CP/M entry point
  36. ;
  37. ;    org    08h        ; RST 1
  38.  
  39.     lxi    sp,boot$stack
  40.     mvi    a,3Fh            ; MMU enable RAM bank 0 no I/O
  41.     jmp    loader$start
  42.  
  43.     page
  44. ;
  45. ;    * TJMP *    user to jmp to a Terminal ROM routine
  46. ;        user code:
  47. ;            RST    2
  48. ;            db    fun#    (0,4,8,C,....,44)
  49. ;
  50. ;    org    10h        ; RST 2
  51.  
  52.     pop    h
  53.     mov    l,m
  54.     jmp    020h
  55.     nop
  56.     nop
  57.     nop
  58.  
  59. ;
  60. ;    * RJMP *    user to jmp to a ROM routine
  61. ;        user code:
  62. ;            RST    3
  63. ;            db    fun#
  64. ;
  65. ;    org    18h        ; RST 3
  66.  
  67.     pop    h            ; get the return address
  68.     mov    l,m            ; get user function # (0,2,...,fe)
  69.     jmp    028h            ; 
  70.     nop
  71.     nop
  72.     nop
  73.  
  74.     page
  75. ;
  76. ;    * TCALL *    used to call a Terminal ROM routine
  77. ;        user code:
  78. ;            mvi    l,fun#    (0,4,8,C,....,44)
  79. ;            RST    4
  80. ;
  81. ;    org    20h        ; RST 4
  82.  
  83.     lda    fun$offset        ; =0 if 80 column, <>0 if 40 column
  84.     ana    a            ; is this an 80 column function?
  85.     jrz    28h            ; yes, no offset required
  86.     inr    l            ; no, advance to next vector
  87.     inr    l
  88.  
  89.  
  90. ;
  91. ;    * RCALL *    used to call a ROM routine
  92. ;        user code:
  93. ;            mvi    l,fun#    (0,2,4,6,.....,7E)
  94. ;            RST    5
  95. ;
  96. ;    org    28h        ; RST 5
  97.  
  98.     mvi    h,01h            ; vectors on page 1
  99.     mov    a,m
  100.     inx    h
  101.     mov    h,m
  102.     mov    l,a
  103.     pchl
  104.     nop
  105.  
  106.     page
  107. ;
  108. ;    RST 6 is NOT defined.. this area is used for the ROM date
  109. ;    ONLY....
  110. ;
  111. ;    org    30h        ; RST 6
  112.  
  113.     db    '05/12/85'
  114.  
  115.  
  116. ;    org    38h        ; RST 7 (interrupt mode 1 start adr)
  117.     jmp    0fdfdh
  118.  
  119.     page
  120. ;
  121. ;
  122. ;
  123. power$up:
  124.     lxi    b,VIC$key$row
  125.     lxi    d,0fffch        ; D=ff, E=fc
  126.     outp    d            ; set extra 3 scan lines off
  127.     inx    b            ; point to clock speed reg
  128.     outp    e            ; bits 7-2 unused
  129.                     ; bit 1  enable test mode (1)
  130.                     ; bit 0  2 Mhz (1) / 1 MHz (0)  
  131. ;
  132. ;    continue the check to see if a C64 type chartrage is installed
  133. ;    (EXROM or GAME active) if so we enter C64 mode
  134. ;
  135.     lxi    b,mode$reg        ; get EXROM and GAME bits
  136.     mvi    a,z80$on
  137.     outp    a            ; set bit high
  138.     inp    a            ; see if they went high
  139.     cma                ; make highs low
  140.     ani    30h            ; EXROM or GAME enabled?
  141.     jrz    test$key        ; no, now test the commodore key
  142.  
  143. ;
  144. ;    This is a one way trip, no return flights.
  145. ;    Thus we do not have to do the transfer from RAM
  146. ;    (as in C128 mode).  We just enable C64 and run. 
  147. ;
  148. go$c64:
  149.     mvi    a,enable$c64
  150.     outp    a
  151.                     ; should never get here
  152.     RST    0            ; unless there are hardware problems
  153.  
  154.     page
  155.  
  156. test$key:
  157.     lxi    b,0dc0fh        ; D1CRB
  158.     mvi    a,8h            ; turn off timers
  159.     outp    a
  160.     dcr    c            ; D1CRA
  161.     outp    a
  162.  
  163.     mvi    c,03h            ; D1DDRB = inputs
  164.     xra    a            ; A=00
  165.     outp    a
  166.     dcr    c            ; D1DDRA = outputs
  167.     dcr    a            ; A=FF
  168.     outp    a
  169.     dcr    c            ; dc01
  170.     dcr    c            ; dc00
  171.     mvi    a,01111111b        ; bit 7 for commodore key
  172.     outp    a
  173.     inx    b            ; dc01 point to key$col
  174.     inp    a
  175.     ani    commodore$key
  176.     lxi    b,mode$reg
  177.     jrz    go$c64
  178.  
  179. go$c128:
  180. ;
  181. ;    set MMU registers to a known state
  182. ;
  183.     lxi    h,mmu$init$data+11-1    ; start at the End
  184.     lxi    b,mmu$start+11-1    ; and work forward
  185.     mvi    d,11            ; for all 11 bytes
  186.  
  187. init$mmu$next:
  188.     mov    a,m            ; get table value
  189.     outp    a            ; send to MMU
  190.     dcx    h
  191.     dcr    c
  192.     dcr    d
  193.     jrnz    init$mmu$next
  194.  
  195.  
  196. ;
  197. ;    install 8502 code that will enable C128 mode and 
  198. ;    execute at the location pointed to by FFFC (reset vector)
  199. ;
  200.     lxi    h,boot$02$code
  201.     lxi    d,boot$8502
  202.     lxi    b,boot$size
  203.     ldir
  204.  
  205.     lxi    h,swap$code
  206.     lxi    d,enable$z80
  207.     lxi    b,swap$size
  208.     ldir
  209.  
  210. ;
  211. ;    Get ready to enter C128 mode.  Install vectors in ram that will
  212. ;    force the processor to execute RAM code in low memory.
  213. ;    The RAM code in low memory ENABLES the kernal and does
  214. ;    an indirect JMP to FFFC (reset vector).
  215. ;
  216.     lxi    h,boot$8502        ; C128 start adr
  217.     shld    0fffah            ; install NMI vector
  218.     shld    0fffch            ; install RESET vector
  219.     shld    0fffeh
  220.     shld    return$z80+1
  221.     jmp    enable$6502
  222.  
  223.     page
  224. ;
  225. ;    scan buffer for CPM+.SYS file
  226. ;
  227. scan$dir:
  228.     call    update$buffer        ; returns HL=block$buffer
  229.     lda    block$size        ; 32 for 1K block, 64 for 2K block
  230.                     ; ..number director entries/sector
  231.  
  232. check$next:
  233.     shld    @dma
  234.     lxi    d,sys$name        ; point to system name
  235.     push    psw
  236.     call    name$match
  237.     cz    found
  238.     pop    psw
  239.     lhld    @dma            ; get current buffer pointer
  240.     lxi    d,32
  241.     dad    d
  242.     dcr    a
  243.     jrnz    check$next
  244.     ret
  245.  
  246.     page
  247. ;
  248. ;    compare the strings (11 bytes each) pointed to by
  249. ;    DE and HL. Return with Zero flag set if equal.  
  250. ;
  251. name$match:
  252.     mvi    b,12            ; number of bytes to match
  253.      xchg                ; [HL]=search name  [DE]=dir entry
  254.  
  255. match$next:
  256.     ldax    d            ; get string 1 character
  257.     ani    7fh            ; remove any attr.
  258.     cmp    m            ; compare to string 2
  259.     rnz                ; exit if they don't match
  260.  
  261.     inx    h
  262.     inx    d
  263.     djnz    match$next
  264.  
  265.     lda    block$size        ;
  266.     cpi    64            ; 2K block?
  267.     ldax    d            ; get the dir ext#
  268.     jrnz    ext$1k            ; no, ext # ok
  269.                     ; yes, (carry=0)
  270.     rar                ; divide by 2, ext could be 0 or 1
  271.                     ; ..for the 1st and 2 or 3 for the
  272. ext$1k:                    ; ..second entry
  273.     sta    ext$num
  274. null$code:
  275.     xra    a            ; return with zero flag set 
  276.     ret
  277.  
  278.     page
  279. ;
  280. ;
  281. ;
  282. sys$name:
  283.         ;  12345678901
  284.     db    0,'CPM+    SYS'        ; must be in user 0's space
  285.  
  286.     db    0
  287. ;
  288. ;
  289. ;
  290. ;    org    0100h-6
  291. cmp$hl$de:
  292.     mov    a,h
  293.     cmp    d
  294.     rnz
  295.     mov    a,l
  296.     cmp    e
  297.     ret
  298.  
  299.     page
  300.  
  301. ;    org    0100h
  302.  
  303.     dw    wr$char$80        ; function # 00
  304.     dw    wr$char$40        ; function # 02
  305.     dw    crs$pos$80        ; function # 04
  306.     dw    crs$pos$40        ; function # 06
  307.     dw    crs$up$80        ; function # 08
  308.     dw    crs$up$40        ; function # 0A
  309.     dw    crs$down$80        ; function # 0C
  310.     dw    crs$down$40        ; function # 0E
  311.  
  312.     dw    crs$left$80        ; function # 10
  313.     dw    crs$left$40        ; function # 12
  314.     dw    crs$rt$80        ; function # 14
  315.     dw    crs$rt$40        ; function # 16
  316.     dw    crs$cr$80        ; function # 18
  317.     dw    crs$cr$40        ; function # 1A
  318.     dw    CEL$80            ; function # 1C
  319.     dw    CEL$40            ; function # 1E
  320.  
  321.     dw    CES$80            ; function # 20
  322.     dw    CES$40            ; function # 22
  323.     dw    char$ins$80        ; function # 24
  324.     dw    char$ins$40        ; function # 26
  325.     dw    char$del$80        ; function # 28
  326.     dw    char$del$40        ; function # 2A
  327.     dw    line$ins$80        ; function # 2C
  328.     dw    line$ins$40        ; function # 2E
  329.  
  330.     dw    line$del$80        ; function # 30
  331.     dw    line$del$40        ; function # 32
  332.     dw    set$color$80        ; function # 34
  333.     dw    set$color$40        ; function # 36
  334.     dw    set$attr$80        ; function # 38
  335.     dw    set$attr$40        ; function # 3A
  336.     dw    rd$chr$80        ; function # 3C
  337.     dw    rd$chr$40        ; function # 3E
  338.  
  339.     page
  340.  
  341.     dw    wr$chr$80        ; function # 40
  342.     dw    wr$chr$40        ; function # 42
  343.     dw    rd$color$80        ; function # 44
  344.     dw    rd$color$40        ; function # 46
  345.     dw    null$code        ; function # 48
  346.     dw    null$code        ; function # 4A
  347.     dw    null$code        ; function # 4C
  348.     dw    null$code        ; function # 4E
  349.  
  350.     dw    convert$record        ; function # 50
  351.     dw    check$cbm        ; function # 52
  352.     dw    bell            ; function # 54
  353.     dw    null$code        ; function # 56
  354.     dw    null$code        ; function # 58
  355.     dw    null$code        ; function # 5A
  356.     dw    null$code        ; function # 5C
  357.     dw    null$code        ; function # 5E
  358.  
  359.     dw    trk$40            ; function # 60
  360.     dw    set$cursor$40        ; function # 62
  361.     dw    line$paint        ; function # 64
  362.     dw    screen$paint        ; function # 66
  363.     dw    prt$msg$both        ; function # 68
  364.     dw    prt$de$both        ; function # 6A
  365.     dw    update$it        ; function # 6C
  366.     dw    null$code        ; function # 6E
  367.  
  368.     dw    ASCII$to$petASCII    ; function # 70
  369.     dw    cur$adr$40$hl$sz$a    ; function # 72
  370.     dw    cur$adr$80$hl$sz$a    ; function # 74
  371.     dw    lookup$color        ; function # 76
  372.     dw    null$code        ; function # 78
  373.  
  374.     dw    blk$fill        ; function # 7A  ret adr, HL on stack
  375.     dw    blk$move        ; function # 7C  ret adr, HL on stack
  376.     dw    char$install$gp        ; function # 7E  ret adr, HL on stack
  377.  
  378. ;    the last 3 function are called by 1st pushing HL on the stack
  379. ;    and then doing the call
  380. ;        user code as follows:
  381. ;            lxi    h,xyz        ; value to be passed in HL 
  382. ;            push    h        ; extra value on stack
  383. ;            RCALL    ....
  384. ;                        ; stack clean
  385.  
  386.     page
  387. ;
  388. ;    org    180h
  389. ;
  390.     jmp    write$memory
  391.     jmp    read$memory
  392.     jmp    set$update$adr
  393.     jmp    wait
  394.  
  395. ;
  396. ;
  397. ;
  398. loader$start:
  399. ;
  400. ;    setup the MMU for booting CP/M
  401. ;
  402.     sta    force$map
  403.  
  404. ;
  405. ;    clear bank 0 RAM 3000h to feffh. This is the system area. 
  406. ;
  407.     lxi    h,3000h
  408.     lxi    d,3001h
  409.     lxi    b,0ff00h-3000h-1
  410.     mov    m,l
  411.     ldir
  412.  
  413. ;
  414. ;    move bios and swap code into ram
  415. ;
  416.     lxi    h,bios$65$code
  417.     lxi    d,bios$02
  418.     lxi    b,bios$size
  419.     ldir
  420.  
  421.     lxi    h,swap$code
  422.     lxi    d,enable$z80
  423.     lxi    b,swap$size
  424.     ldir
  425.  
  426.     mvi    a,RET            ; get z80 return adr
  427.     sta    return$6502        ; store the RET
  428.  
  429. ;
  430. ;    initilize the 8502 bios
  431. ;
  432. ; xra    a                ; cleared by memory fill
  433. ; sta    vic$cmd
  434.     call    enable$6502
  435.  
  436.     page
  437. ;
  438. ;    set MMU registers to a known state (for CP/M to use)
  439. ;
  440.     lxi    h,mmu$init$data+11-1    ; start at the End
  441.     lxi    b,mmu$start+11-1    ; and work forward
  442.     mvi    d,11            ; for all 11 bytes
  443.  
  444. init$mmu$cpm:
  445.     mov    a,m            ; get table value
  446.     outp    a            ; send to MMU
  447.     dcx    h
  448.     dcr    c
  449.     dcr    d
  450.     jrnz    init$mmu$cpm
  451.                     ; re-enabled RAM bank 0 (no I/O)
  452. ;
  453. ;    Clear the work area
  454. ;
  455.     lxi    h,1000h
  456.     lxi    d,1000h+1
  457.     lxi    b,3000h-1000h-1        ; number of bytes to clear
  458.     mov    m,l            ; clear the 1st one
  459.     ldir                ; copy 1st to all
  460.  
  461.     page
  462. ;
  463. ;    set 80 column colors and set up Video memory with ASCII char set
  464. ;
  465.     mvi    a,26
  466.     call    wait
  467.     mvi    a,90h            ; foreground red background black
  468.     outp    a
  469.     mvi    a,83h            ; set attr and color (lt. blue)
  470.     sta    current$atr        ; ..for 80 column display
  471.     mvi    a,0eh            ; set color (lt. blue)
  472.     sta    attr$40            ; ..for 40 column display
  473.     call    install$ASCII        ; convert char set to true ASCII
  474.  
  475.     mvi    a,25            ; number of lines on the 40 col display
  476.     sta    paint$size
  477. ;
  478. ;    Let the user know we are booting CP/M
  479. ;
  480.     call    prt$msg$both
  481.     db    -1            ; clear both screens
  482.     rownum    1,10
  483.     db    'BOOTING CP/M PLUS',0
  484.  
  485. ;
  486. ;    point 40 column screen to CP/M screen area
  487. ;
  488.     lxi    b,VIC+24
  489.     mvi    a,vic$screen*4/256+6    ; upper and lower case set (+6)
  490.     outp    a
  491.  
  492.     page
  493.  
  494.     call    check$dsk        ; is this a C128 disk ?
  495.     jnz    tell$user        ; no, tell the user
  496. ;
  497. ;
  498. ;
  499.     lxi    h,dir$ptrs
  500.     shld    ld$blk$ptr
  501.     call    scan$dir        ; check 1st block
  502.     call    scan$dir        ; check 2nd block (1K or 2K)
  503.  
  504.     lhld    block$ptrs        ; 1st pointer <>0 if file
  505.     mov    a,h            ; name exist
  506.     ora    l            ; pointer = 0
  507.     jz    tell$user        ; yes, inform user there is a error
  508.                     ; no, file found, process it
  509.     page
  510. ;    *************************************************
  511. ;    *                        *
  512. ;    *    load 1st group to 1K buffer        *
  513. ;    *                        *
  514. ;    *************************************************
  515.  
  516. ;
  517. ;
  518. ;
  519. file$found:
  520.     lxi    h,block$ptrs
  521.     shld    ld$blk$ptr
  522.     call    update$buffer
  523.  
  524.  
  525. ;    *************************************************
  526. ;    *                        *
  527. ;    *    extract boot info            *
  528. ;    *                        *
  529. ;    *************************************************
  530.  
  531. ;
  532. ;
  533. ;
  534. get$boot$info:
  535.     lxi    h,block$buffer
  536.     lxi    d,info$buffer
  537.     lxi    b,12
  538.     ldir
  539.  
  540.     call    prt$msg$both
  541.     rownum    10,0
  542.     db    0            ; end of string marker
  543.     lxi    h,block$buffer+80h
  544.     call    prt$hl$both
  545.  
  546.     lxi    h,block$buffer+256    ; set scan pointer
  547.     shld    blk$unld$ptr
  548.  
  549.     page
  550. ;    *************************************************
  551. ;    *                        *
  552. ;    *    load keyboard data to system RAM    *
  553. ;    *                        *
  554. ;    *************************************************
  555.  
  556.     call    prt$msg$both
  557.     rownum    3,12
  558. ;    db    'LOADING DATA TABLES',0
  559.     db    'DATA TABLES',0
  560.     lhld    info$buffer+10
  561.     shld    key$tbl            ; install keyboard translation pointer
  562.  
  563.     lxi    h,info$buffer+9
  564.     call    get$size$adr        ; HL=adr DE=# (128 btye) records 
  565.     shld    fun$tbl
  566.  
  567. load$next$forward:
  568.     call    load$record        ; HL =load address (in and out)
  569.     lxi    d,128            ; move pointer back to buf start
  570.     dad    d
  571.     jrnz    load$next$forward
  572.  
  573.  
  574.     page
  575. ;    *************************************************
  576. ;    *                        *
  577. ;    *    transfer CP/M code to load address    *
  578. ;    *                        *
  579. ;    *************************************************
  580. ;
  581. ;
  582. ;
  583. load$common:
  584.     call    prt$msg$both
  585.     rownum    4,12
  586. ;    db    'LOADING COMMON CODE',0
  587.     db    'COMMON CODE',0
  588.     lxi    h,info$buffer+1        ; load common code
  589.     call    load$reverse
  590.  
  591.  
  592.     call    prt$msg$both
  593.     rownum    5,12
  594. ;    db    'LOADING BANKED CODE',0
  595.     db    'BANKED CODE',0
  596.     lxi    h,info$buffer+3        ; load banked code
  597.     call    load$reverse
  598.  
  599.     page
  600. ;    *************************************************
  601. ;    *                        *
  602. ;    *    now load the bios8502 code        *
  603. ;    *                        *
  604. ;    *************************************************
  605.  
  606.     call    prt$msg$both
  607.     rownum    6,12
  608. ;    db    'LOADING BIOS8502 CODE',0
  609.     db    'BIOS8502 CODE',0
  610.     lxi    h,info$buffer+7        ; load banked code
  611.     call    load$reverse
  612.  
  613.     lda    info$buffer+7        ; get code size (in 256 byte blocks)
  614.     mov    b,a
  615.     lda    info$buffer+6        ; get page pointer (pointer to end)
  616.     sub    b            ; find the start
  617.  
  618.                     ; install jmp adr to BIOS02
  619.     sta    return$z80+2        ; (jmp) (low) (high)
  620.     xra    a
  621.     sta    return$z80+1        ; (jmp) (low) (high)
  622.  
  623.  
  624. ;    *************************************************
  625. ;    *                        *
  626. ;    *    now let's start executing CP/M Plus    *
  627. ;    *                        *
  628. ;    *************************************************
  629.  
  630.     lhld    info$buffer+4        ; get start address
  631.     pchl                ; transfer control to CP/M
  632.  
  633.     page
  634. ;    *********************************
  635. ;    -                -
  636. ;    -       SUBROUTINES        -
  637. ;    -                -
  638. ;    *********************************
  639.  
  640. ;
  641. ;    returns with zero flag set if bootable disk in drive
  642. ;
  643. check$dsk:
  644.     lxi    h,@buffer
  645.     shld    @dma
  646.     xra    a
  647.     sta    vic$sect        ; set track 1 sector 0 (1st sector
  648.     inr    a            ; on the disk)
  649.     sta    vic$trk
  650.     call    read$sector        ; a=0 if no errors
  651.     call    check$cbm        ; disk have CBM in first sector ?
  652.     rnz                ; no, exit
  653.     inr    a            ; yes, is it double sided?
  654.     lxi    h,block$buffer+1024    ;   buffer end address (1K blocks)
  655.     mvi    a,32            ;   number of dir entries per block
  656.     jrnz    set$block$size        ;  yes, set it
  657.                     ;  no, set 2K block parameters
  658.     mvi    h,high(block$buffer+2048)
  659.     add    a            ; 64 dir entries
  660.  
  661. set$block$size
  662.     shld    block$end
  663.     sta    block$size        ; 32=1K,  64=2K (# dir entries/block)
  664.     xra    a            ; set zero flag (is CP/M disk)
  665.     ret
  666.  
  667.     page
  668. ;    *************************************************
  669. ;    -                        -
  670. ;    -    save CPM+.SYS group numbers        -
  671. ;    -                        -
  672. ;    *************************************************
  673. ;
  674. ;    found a dir entry that has the right name
  675. ;    add block pointers to list
  676. ;
  677. found:
  678.     lxi    d,block$ptrs        ; point to start of block pointers
  679.     lda    ext$num
  680.     ora    a
  681.     jrz    ext$num$0
  682.  
  683.     lxi    d,block$ptrs+16
  684.     dcr    a
  685.     jnz    ext$error
  686.  
  687. ext$num$0:
  688.     lhld    @dma            ; get current pointer
  689.  
  690.     lxi    b,16            ; number of bytes to move
  691.     dad    b            ; also advance to block pointers
  692.     ldir
  693.  
  694.     lda    block$ptrs
  695.     ora    a            ; 1st block present ?
  696.     rz                ; no, read more dir.
  697.  
  698.     lhld    block$ptrs+15        ; extent full?
  699.     xra    a            ; get a zero
  700.     cmp    l            ; cmp to block$prt+15
  701.     jrz    go$boot$it        ; no, this is it then
  702.  
  703.     cmp    h            ; cmp to block$prt+16
  704.                     ; 2nd block present ?
  705.     rz                ; no, read more dir.
  706.  
  707. go$boot$it:
  708.     jmp    file$found        ; two parms are still on the stack
  709.                     ; but at this point who cares
  710.  
  711.     page
  712. ;
  713. ;
  714. ;
  715. load$reverse:
  716.     call    get$size$adr        ; HL=adr DE=# records (128 byte)
  717.  
  718. load$next:
  719.     lxi    d,-128            ; move pointer back to buf start
  720.     dad    d
  721.     call    load$record
  722.     jrnz    load$next
  723.     ret
  724.  
  725. ;
  726. ;
  727. ;
  728. get$size$adr:
  729.     mov    e,m
  730.     mvi    d,0            ; get buffer size (#256 byte)
  731.     mov    a,e            ; get size to A
  732.     ora    a
  733.     jz    table$error        ; exit if count=0
  734.  
  735.     xchg
  736.     dad    h            ; HL=#128 byte blocks
  737.     shld    load$count
  738.     xchg
  739.     dcx    h
  740.     mov    h,m
  741.     mvi    l,0
  742.     ret
  743.  
  744.     page
  745. ;
  746. ;
  747. ;
  748. load$record:
  749.     push    h            ; save to address
  750.     lhld    block$end        ; get buffer end adr    
  751.     xchg
  752.     lhld    blk$unld$ptr
  753.     call    cmp$hl$de
  754.     cz    update$buffer
  755.     xchg
  756.     lxi    h,128
  757.     dad    d
  758.     shld    blk$unld$ptr
  759.     pop    h            ; recover save address
  760.     push    h
  761.     xchg                ; HL=source  DE=dest.
  762.     lxi    b,128            ; size of move
  763.     ldir
  764.     lhld    load$count
  765.     dcx    h
  766.     shld    load$count
  767.     mov    a,l
  768.     ora    h
  769.     pop    h
  770.     ret
  771.  
  772.     page
  773. ;
  774. ;
  775. ;
  776. update$buffer:
  777.     lxi    h,block$buffer
  778.     shld    @dma
  779.     push    h            ; save block buffer adr for ret
  780.     lhld    ld$blk$ptr        ; get the current block pointer
  781.     mvi    d,0            ; zero MSB of block pointer
  782.     mov    e,m            ; get LSB of block pointer
  783.     inx    h            ; advance pointer
  784.     shld    ld$blk$ptr
  785.     xchg                ; get block number to HL
  786. ;
  787. ;    read the block pointed to by the HL
  788. ;    into the data buffer
  789. ;
  790.     dad    h            ; 2x
  791.     dad    h            ; 4x 256=1K
  792.     lda    block$size        ; =32 for 1K, =64 for 2K
  793.     rrc
  794.     rrc
  795.     rrc                ; 32/8=4,  64/8=8
  796.     cpi    32/8            ; 1K block size?
  797.     jrz    is$1K$block
  798.     dad    h            ; 8x 256=2K
  799. is$1K$block:
  800.     shld    @trk
  801. next$block:
  802.     dcr    a            ; 3 or 7 sectors left to read
  803.     sta    vic$count        ; ..1st sector is read anyway
  804.     push    psw
  805.     mvi    a,1
  806.     sta    F$rd$count+BB        ; 
  807.  
  808.     call    convert$record        ; set track, sector (adjust for offset)
  809.     lhld    @trk
  810.     inx    h
  811.     shld    @trk            ; save for later
  812.     pop    psw
  813.     jrz    rd$1541
  814.  
  815.     lda    fast
  816.     ana    a            ; 0=1541,  0<>1571
  817.     jrz    rd$1541
  818.  
  819.     lhld    vic$trk            ; get track and sector #
  820.     push    h            ; save on stack
  821. check$next$trk:
  822.     call    convert$record        ; convert next track and sector
  823.     pop    h            ; recover 1st trk and sector #
  824.  
  825.     lda    vic$trk            ; get trk$number
  826.     cmp    l            ; same trk as 1st sector
  827.     jrnz    not$same$trk
  828.  
  829.     push    h            ; resave 1st trk and sect
  830.     lhld    @trk
  831.     inx    h
  832.     shld    @trk
  833.     lxi    h,F$rd$count+BB
  834.     inr    m
  835.     lxi    h,vic$count
  836.     dcr    m
  837.     jrnz    check$next$trk
  838.     pop    h
  839.  
  840. not$same$trk:
  841.     shld    vic$trk            ; save the 1st track sector #
  842. rd$1541:
  843.     call    read$sector        ; read the sector to the buffer
  844.     lxi    h,@dma+1        ; point to dma high byte
  845.     lda    F$rd$count+BB
  846.     add    m
  847.     mov    m,a            ; adjust for next read
  848.     lda    vic$count
  849.     ana    a            ; test if all sectors read?
  850.     jrnz    next$block        ; no loop back
  851.  
  852.     pop    h            ; recover block buffer adr
  853.     ret
  854.  
  855.     page
  856. ;
  857. ;    convert block number to sector and track
  858. ;
  859. convert$record:
  860.     mvi    a,35
  861.     sta    temp$1            ; store a track offset of 35
  862.     lhld    @trk            ; get start block #
  863.     lxi    d,680            ; 0 to 680 sectors per side
  864.     ora    a            ; clear the carry
  865.     dsbc    d            ; negative if <680
  866.     jrnc    side$1            ; jump if still positive >=680
  867.     xra    a            ;
  868.     sta    temp$1            ; store a track offset of 0
  869.     dad    d            ; add it back
  870.  
  871. side$1:
  872.     inx    h            ; skip 1st sector (both sides)
  873.     inx    h            ; skip 2nd sector (both sides)
  874.     lxi    d,357            ; get first value to subtract
  875.     lxi    b,21*256+1-1        ; b=sectors/track c=track offset-1
  876.     ora    a            ; clear the carry bit
  877.     dsbc    d            ; 
  878.     jrc    too$much        ; 
  879.     inx    h            ; add 1 to skip track 18 sector 0
  880.     lxi    d,490-357        ; get first value to subtract
  881.     lxi    b,19*256+18-1        ; b=sectors/track c=track offset-1
  882.     dsbc    d            ; 
  883.     jrc    too$much        ; 
  884.     lxi    d,598-490        ; get first value to subtract
  885.     lxi    b,18*256+25-1        ; b=sectors/track c=track offset-1
  886.     dsbc    d            ; 
  887.     jrc    too$much        ; 
  888.     lxi    d,0
  889.     lxi    b,17*256+31-1        ; b=sectors/track c=track offset-1
  890.  
  891.     page
  892. ;
  893. ;    at this point B= number of sectors/track and C=track offset
  894. ;    after DE is added back to HL (1st inst), HL is the number of
  895. ;    sector past the current track (in C).
  896. ;
  897. too$much:
  898.     dad    d            ; add back what made sum go negitive
  899.     mvi    d,0            ; number of sectors/track in DE
  900.     mov    e,b
  901.     ora    a            ; clear the carry bit
  902. sect$pos:
  903.     inr    c            ; add one to the current track (1-35)
  904.     dsbc    d            ; remove a track's worth of sectors
  905.     jrnc    sect$pos        ; less then one?, no jmp
  906.     dad    d            ; make HL positive again
  907. ;
  908. ;    at this point HL has the remainder (sector # 0-20) and
  909. ;    C has the track number (1-35), DE and B still has the
  910. ;    # sectors/track    for the current track.
  911. ;
  912.     lda    temp$1            ; get track offset
  913.     add    c            ; add to current track
  914.     sta    vic$trk            ; save it
  915.     push    h
  916.     lxi    h,special$skew
  917.     lxi    b,21            ; number of sectors in 1st region
  918.     mov    a,e
  919.     cmp    c
  920.     jrz    correct$region
  921.     dad    b            ; move past current region
  922.     dcx    b
  923.     dcx    b            ; 19
  924. adjust$loop:
  925.     cmp    c
  926.     jrz    correct$region
  927.     dad    b
  928.     dcx    b
  929.     jr    adjust$loop
  930. ;
  931. ;
  932. ;
  933. correct$region:
  934.     pop    b            ; get logical sector # in BC
  935.     dad    b
  936.     mov    a,m            ; get translated sector number to A
  937.     sta    vic$sect        ; value from 0 to 20 will be returned
  938.     inr    a            ; A is required to be non-zero on ret 
  939.     ret
  940.  
  941.     page
  942. ;
  943. ;
  944. ;
  945. read$sector:
  946.     mvi    a,3
  947.     sta    retry
  948.  
  949. read$again:
  950.     mvi    a,vicrd        ; read a sector of data
  951.     sta    vic$cmd
  952.     call    disp$dsk$info
  953.     call    enable$6502
  954.     mvi    a,3fh        ; mmu ram bank 0 (no I/O)
  955.     sta    force$map
  956.     lda    vic$data
  957.     ora    a        ; problems?
  958.     jrnz    read$error    ; yes, check for disk error or media change
  959.                 ; no, go move buffer to DMA address
  960.     ret
  961.  
  962.     page
  963. ;
  964. ;
  965. ;
  966. check$cbm:
  967.     lxi    h,@buffer
  968.     mov    a,m
  969.     cpi    'C'            ; C ?
  970.     rnz                ; no, return
  971.  
  972.     inr    l            ; @buffer+1
  973.     mov    a,m
  974.     cpi    'B'            ; B ?
  975.     rnz                ; no, return
  976.  
  977.     inr    l            ; @buffer+0
  978.     mov    a,m
  979.     cpi    'M'            ; M ?
  980.     rnz
  981.  
  982.     mvi    l,0ffh            ; @buffer+0ffh
  983.                     ; point to the double sided flag
  984.     mov    a,m            ; read it, 0FFh if double sided
  985.     ret
  986.  
  987.     page
  988. ;
  989. ;
  990. ;
  991. ext$error:
  992.     call    prt$msg$both
  993.     rownum    19,5
  994.     db    '32K MAX CPM+.SYS SIZE',0
  995.  
  996. try$again:
  997.     rst    1
  998.  
  999. ;
  1000. ;
  1001. ;
  1002. read$error:
  1003.     inr    a        ; test for -1
  1004.     jrz    try$again
  1005.  
  1006.     lda    retry
  1007.     dcr    a
  1008.     sta    retry
  1009.     jrnz    read$again
  1010.  
  1011.  
  1012.  
  1013.     call    prt$msg$both
  1014.     rownum    19,5
  1015.     db    'READ ERROR',0
  1016. error$2:
  1017.     call    prt$msg$both
  1018.     db    ' - HIT RETURN TO RETRY'
  1019.     rownum    20,15
  1020.     db    'DEL TO ENTER C128 MODE',0
  1021.  
  1022. ;
  1023. ;
  1024. ;
  1025. wait$key:
  1026.     lxi    b,key$row
  1027.     mvi    a,11111110b        ; bit 0 for RETURN key
  1028.     outp    a
  1029.     inr    c            ; point to key$col
  1030.     inp    a
  1031.     ani    2            ; on bit 1 (0-7)
  1032.     jrz    try$again
  1033.  
  1034.     inp    a
  1035.     ani    1            ; delete key down
  1036.     jrnz    wait$key        ; no, wait for RETURN key
  1037.  
  1038.     rst    0            ; yes, reboot C128-mode
  1039.  
  1040.  
  1041.     page
  1042. ;
  1043. ;    CPM+.SYS file not on this disk test user to install
  1044. ;    a system disk and wait for a CR to continue
  1045. ;
  1046. tell$user:
  1047.     call    prt$msg$both
  1048.     rownum    19,5
  1049.     db    'NO',0
  1050. error$1:
  1051.     call    prt$msg$both
  1052.     db    ' CPM+.SYS FILE',0
  1053.     jr    error$2
  1054.  
  1055. ;
  1056. ;
  1057. ;
  1058. table$error:
  1059.     call    prt$msg$both
  1060.     rownum    19,5
  1061.     db    'BAD',0
  1062.     jr    error$1
  1063.  
  1064.     page
  1065.  
  1066. ;
  1067. ;
  1068. ;
  1069. prt$msg$both:
  1070.     xthl
  1071.     call    prt$hl$both
  1072.     xthl
  1073.     ret
  1074.  
  1075. update$it:
  1076.     lxi    h,-1
  1077.     shld    old$offset        ; force an update
  1078. ;
  1079. ;
  1080. ;
  1081. prt$de$both:
  1082.     push    d
  1083. ;
  1084. ;
  1085. ;
  1086. prt$hl$both$loop:
  1087.     pop    h
  1088. ;
  1089. ;
  1090. ;
  1091. prt$hl$both:
  1092.     mov    d,m
  1093.     inx    h        ; advance the pointer
  1094.     lda    prt$flg
  1095.     ana    a
  1096.     jrz    no$flag
  1097.     xra    d
  1098.     sta    prt$flg
  1099.     mov    d,a
  1100. no$flag:
  1101.     mov    a,d
  1102.     ora    a
  1103.     rz
  1104.  
  1105.     cpi    '$'
  1106.     rz            ; yes, return
  1107.  
  1108.     push    h
  1109.     lxi    h,prt$hl$both$loop
  1110.     push    h    
  1111.     cpi    LF
  1112.     rz
  1113.  
  1114.     page
  1115. ;
  1116. ;
  1117. ;
  1118. check$both$CR:
  1119.     cpi    CR
  1120.     jrnz    check$erase$both
  1121. ;
  1122. ;
  1123. do$crlf$both:
  1124.     call    crs$cr$40
  1125.     call    crs$cr$80
  1126.     call    crs$down$40
  1127.     RJMP    FR$cursor$down        ; cursor down 80
  1128.  
  1129. ;
  1130. ;
  1131. ;
  1132. check$erase$both:
  1133.     cpi    -1
  1134.     jrnz    check$both$CUR$$POS
  1135. ;
  1136. ;    Erase both screens
  1137. ;
  1138.     lxi    d,24*256        ; erase the status line 1st
  1139.     call    curpos$BC$both
  1140.     call    CEL$40
  1141.     call    CEL$80
  1142.     lxi    d,0            ; erase main screen
  1143.     call    curpos$BC$both
  1144.     call    CES$40
  1145.     RJMP    FR$CES
  1146.  
  1147.  
  1148. check$both$CUR$POS:
  1149.     ani    80h
  1150.     jrz    out$d$both
  1151. ;
  1152. curpos$D$both:
  1153.     pop    b            ; get return adr in BC
  1154.     pop    h            ; get pointer in HL
  1155.     mov    e,m            ; E=column #
  1156.     inx    h
  1157.     push    h            ; save new pointer
  1158.     push    b            ; save return adr
  1159.     res    7,d            ; D=row #
  1160.  
  1161. curpos$BC$both:
  1162.     push    d            ; save cursor address 
  1163.     call    crs$pos$40
  1164.     pop    d
  1165.     RJMP    FR$cursor$pos        ; 80 column
  1166.  
  1167.  
  1168.     page
  1169. ;
  1170. ;
  1171. ;
  1172. disp$dsk$info:
  1173.     lxi    d,24*256+80-6
  1174.     call    crs$pos$80
  1175.     lxi    d,24*256+40-6
  1176.     call    crs$pos$40
  1177.  
  1178.  
  1179.     lda    vic$trk
  1180.     call    disp$dec
  1181.     mvi    d,' '
  1182.     call    out$d$both
  1183.     lda    vic$sect
  1184.  
  1185. disp$dec:
  1186.     mvi    b,'0'-1
  1187.  
  1188. conv$loop:
  1189.     inr    b
  1190.     sui    10
  1191.     jrnc    conv$loop
  1192.  
  1193.     adi    '0'+10
  1194.     push    psw
  1195.     mov    a,b
  1196.     call    disp$A
  1197.     pop    psw
  1198.  
  1199. disp$A:
  1200.     mov    d,a
  1201. ;
  1202. ;
  1203. ;
  1204. out$d$both:
  1205.     push    d
  1206.     call    wr$char$40
  1207.     pop    d
  1208.     RJMP    FR$wr$char
  1209.  
  1210.  
  1211.