10 REM > BIOS/src
   20 REM Source for ZNOS BIOS
   30 :
   40 A%=0:X%=1:os%=(USR&FFF4 AND &FF00)DIV256:IFos%=6 AND PAGE>&8000:PRINT"Running Z80...":CASEATN "OS_GetEnv" TO A$:OSCLI"TextToBas "+MID$(A$,INSTR(A$," ",1+INSTR(A$," ")))+" -crunch *Z80":END
   50 :
   60 load%=&F200:DIM mcode% &400
   70 :
   80 FOR P=0 TO 1
   90   P%=load%:O%=mcode%
  100   [OPT P*3+4
  110   ; ZNOS BIOS Source Code
  120   ; =====================
  130   
  140   ; Commentary Copyright (C) J.åHarston
  150   ; See mdfs.net/Software/CPM/ZNOS
  160   ;
  170   .BIOS_CBOOT  :JP   ColdBoot
  180   .BIOS_WBOOT  :JP   WarmBoot
  190   .BIOS_CONST  :JP   ConsoleStatus
  200   .BIOS_CONIN  :JP   ConsoleIn
  210   .BIOS_CONOUT :JP   ConsoleOut
  220   .BIOS_LIST   :JP   List
  230   .BIOS_PUNCH  :JP   PunchOut
  240   .BIOS_READER :JP   ReaderIn
  250   .BIOS_HOME   :JP   DiskNull
  260   .BIOS_SELDSK :JP   DiskNull
  270   .BIOS_SETTRK :JP   DiskNull
  280   .BIOS_SETSEC :JP   DiskNull
  290   .BIOS_SETDMA :JP   DiskNull
  300   .BIOS_READ   :JP   DiskNull
  310   .BIOS_WRITE  :JP   DiskNull
  320   .BIOS_LISTST :JP   ListStatus
  330   .BIOS_SECTRAN:JP   DiskNull
  340   :
  350   .PatchArea
  360   NOP
  370   LD   BC,&2424
  380   INC  H
  390   JR   NZ,LF25A
  400   JR   NZ,LF25C
  410   JR   NZ,LF291
  420   LD   D,L
  430   LD   B,D
  440   NOP
  450   NOP
  460   NOP
  470   NOP
  480   DEC  DE
  490   LD   A,(&7E34)
  500   DEC  A
  510   JP   NZ,&2BAB
  520   LD   HL,&3BEC
  530   LD   A,(HL)
  540   OR   A
  550   JP   NZ,&2B34
  560   LD   HL,&3B4A
  570   LD   A,(HL)
  580   JP   &0000
  590   
  600   .LF25B
  610   EQUB 'DEC  HL'
  620   .LF25C
  630   DEFB &21         ; Pending input buffer
  640   .LF25D
  650   DEFB &58         ; 0=no input pending, <>0=input pending
  660   
  670   .LF25E           ; Reset input stream
  680   LD   A,&83       ; Specify CON=UC1 RDR=TTY PUN=TTY LST=LPT
  690   LD   (&0003),A   ; Reset IOBYTE
  700   XOR  A
  710   LD   (LF25D),A   ; Clear a flag
  720   LD   L,&40
  730   LD   A,&03
  740   CALL OSBYTE      ; Output stream=prn disabled
  750   LD   L,&02
  760   LD   A,&02
  770   JP   OSBYTE      ; Input stream=kbd, serial enabled
  780   :
  790   .ConsoleStatus
  800   CALL LF2D5:DEFB &81
  810   DEFW LF373:DEFW LF330:DEFW LF2C9:DEFW LF2F7
  820   :
  830   .ConsoleIn
  840   CALL LF2D5:DEFB &01
  850   DEFW LF381:DEFW LF33D:DEFW LF2BD:DEF LF31D
  860   :
  870   .ConsoleOut
  880   CALL LF2D5:DEFB &81
  890   DEFW LF385:DEFW LF355:DEFW LF299:DEFW LF32C
  900   :
  910   .List
  920   CALL LF2D5:DEFB &03
  930   DEFW LF385:DEFW LF355:DEFW LF38F:DEFW LF38F
  940   :
  950   .ListStatus
  960   CALL LF2D5:DEFB &03
  970   DEFW LF38A:DEFW LF37C:DEFW LF3A6:DEFW LF3A6
  980   :
  990   .PunchOut
 1000   CALL LF2D5:DEFB &05
 1010   DEFW LF385:DEFW LF355:DEFW LF2F6:DEFW LF2F6
 1020   :
 1030   .ReaderIn
 1040   CALL LF2D5:DEFB &07
 1050   DEFW LF381:DEFW LF33D:DEFW LF2F4:DEFW LF2F4
 1060   :
 1070   .LF2C9
 1080   CALL LF2D5:DEFB &07
 1090   DEFW LF373:DEFW LF330:DEFW LF37E:DEFW LF37E
 1100   :
 1110   ; I/O dispatch
 1120   ; ------------
 1130   .LF2D5
 1140   POP  HL:LD A,(HL)   ; Get IOBYTE flag byte
 1150   INC  HL             ; Point to address block
 1160   BIT  7,A:LD B,A
 1170   RES  7,B            ; B=IOBYTE shift amount
 1180   JR   NZ,LF2E3       ; b7=1, don't clear flag
 1190   XOR  A:LD (LF25B),A ; Clear a flag
 1200   .LF2E3
 1210   LD   A,(&0003)      ; Get IOBYTE
 1220   .LF2E6
 1230   RLCA:DJNZ LF2E6     ; Rotate IOBYTE by factor
 1240   AND  &06            ; Keep resultant two bits
 1250   LD   D,&00:LD E,A
 1260   ADD  HL,DE          ; Index into address block
 1270   LD   E,(HL):INC HL
 1280   LD   D,(HL)         ; DE=selected address
 1290   EX   DE,HL:JP (HL)  ; Jump to routine
 1300   
 1310   ; ReaderIn 2,3
 1320   ; ------------
 1330   .LF2F4
 1340   LD A,&1A:RET        ; Return Å
 1350   
 1360   ; ConStatus 3
 1370   ; -----------
 1380   .LF2F7
 1390   LD   A,(LF25D)      ; Is pending buffer full?
 1400   OR   A:RET  NZ      ; Return NZ if input pending
 1410   LD   HL,LF25B       ; Point to something
 1420   XOR  A
 1430   OR   (HL)
 1440   JR   Z,LF306        ; If zero, check host input
 1450   DEC  (HL)           ; Decrement
 1460   XOR  A:RET          ; Return Z - nothing pending
 1470   .LF306
 1480   LD   (HL),&0C
 1490   LD   HL,&0000
 1500   LD   A,&81
 1510   CALL OSBYTE             ; ¦(0), fetch any pending input
 1520   LD   A,H
 1530   INC  A
 1540   RET  Z                  ; Return Z if no input pending
 1550   LD   A,L:LD (LF25C),A   ; Save character in input buffer
 1560   LD   A,&FF:LD (LF25D),A ; Flag input buffer full
 1570   RET                     ; Return NZ, input pending
 1580   
 1590   
 1600   ; ConIn 3
 1610   ; -------
 1620   .LF31D
 1630   LD   A,(LF25D):OR A     ; Check pending buffer
 1640   JP   Z,OSRDCH           ; Empty, jump to OSRDCH
 1650   XOR  A:LD (LF25D),A     ; Clear pending buffer
 1660   LD   A,(LF25C):RET      ; Get character from pending buffer
 1670   
 1680   
 1690   ; ConOut 3
 1700   ; --------
 1710   .LF32C
 1720   LD   A,C:JP &FF9E       ; Jump to TERMOUT
 1730   
 1740   
 1750   ; ConStat 1
 1760   ; ---------
 1770   .LF330
 1780   LD   L,&00
 1790   LD   A,&80
 1800   CALL OSBYTE             ; –(0)
 1810   LD   A,L
 1820   OR   A
 1830   RET  Z
 1840   LD   A,&FF
 1850   RET
 1860   
 1870   .LF33D
 1880   LD   L,&02
 1890   .LF33F
 1900   LD   A,&02
 1910   CALL OSBYTE
 1920   CALL OSRDCH
 1930   PUSH AF
 1940   LD   A,L
 1950   AND  A
 1960   JR   NZ,LF34E
 1970   LD   L,&02
 1980   .LF34E
 1990   LD   A,&02
 2000   CALL OSBYTE
 2010   POP  AF
 2020   RET
 2030   
 2040   .LF356
 2050   LD   HL,&00F4
 2060   .LF358
 2070   LD   A,&03
 2080   CALL OSBYTE
 2090   PUSH HL
 2100   XOR  A
 2110   CALL &FFC8
 2120   PUSH AF
 2130   LD   A,C
 2140   CALL OSWRCH
 2150   POP  AF
 2160   CALL &FFC8
 2170   POP  HL
 2180   LD   H,&00
 2190   LD   A,&03
 2200   JP   OSBYTE
 2210   
 2220   .LF373
 2230   LD   L,&01
 2240   LD   A,&98
 2250   CALL OSBYTE
 2260   JR   NC,LF37E
 2270   XOR  A
 2280   RET
 2290   
 2300   .LF37E
 2310   XOR  A
 2320   DEC  A
 2330   RET
 2340   
 2350   .LF381
 2360   LD   L,&01
 2370   JR   LF33F
 2380   .LF385
 2390   LD   HL,&00F7
 2400   JR   LF358
 2410   .LF38A
 2420   LD   HL,&FFFD
 2430   JR   LF3A9
 2440   .LF38F
 2450   LD   A,&75
 2460   CALL OSBYTE
 2470   BIT  0,L
 2480   JR   NZ,LF39D
 2490   LD   A,&02
 2500   CALL OSWRCH
 2510   .LF39D
 2520   LD   A,&01
 2530   CALL OSWRCH
 2540   LD   A,C
 2550   JP   OSWRCH
 2560   LD   HL,&FFFC
 2570   .LF3A9
 2580   LD   A,&80
 2590   CALL OSBYTE
 2600   LD   A,L
 2610   AND  A
 2620   RET  Z
 2630   XOR  A
 2640   DEC  A
 2650   RET
 2660   
 2670   ;; COLD BOOT
 2680   ;; =========
 2690   ;;
 2700   .ColdBoot             ; Entered on startup
 2710   LD   SP,&0080         ; Put initial stack in CPM workspace
 2720   EI
 2730   XOR  A:LD (&0004),A   ; Clear Current Drive+User
 2740   LD   HL,&F730         ; Point to "1.20" in v1.20 client MOS
 2750   LD   DE,LF3E4         ; Point to "1.20" string
 2760   LD   B,&04            ; Match four characters
 2770   .LF3C4
 2780   LD   A,(DE):CP (HL)   ; Compare strings
 2790   INC  HL:INC DE
 2800   JR   C,LF3D1          ; Version string doesn't match
 2810   DJNZ LF3C4            ; Loop for four characters
 2820   LD   HL,&FCA3         ; v1.20 MOS command flag
 2830   JR   LF3D4
 2840   .LF3D1                ; Not version 1.20 Z80 MOS
 2850   LD   HL,&FF8A         ; v2.00 MOS command flag
 2860   .LF3D4
 2870   LD   (HL),&FF         ; Set to 'no MOS commands'
 2880   CALL LF401            ; Initialise things, error handler, esc state,
 2890                         ; zero page jumps
 2900   CALL LF25E            ; Initialise input and output streams
 2910   CALL &E00C            ; Call BDOS
 2920   LD   C,&00:JP &D800   ; Enter CCP ColdBoot entry with C=drive 0/user 0
 2930   
 2940   .LF3E4
 2950   DEF"1.20"           ; Version string to match
 2960   
 2970   
 2980   ;; WARM BOOT
 2990   ;; =========
 3000   ;;
 3010   .WarmBoot
 3020   LD   SP,&0080         ; Put initial stack in CPM workspace
 3030   EI
 3040   LD A,&03:CALL OSWRCH  ; End any print job
 3050   CALL &E00F            ; Call BDOS
 3060   CALL LF3FC            ; Fetch CCP and reset vectors
 3070   LD   C,&00:JP &D803   ; Enter CCP WarmBoot entry with C=drive 0/user 0
 3080   
 3090   ; Fetch CCP and initialise vectors
 3100   ; --------------------------------
 3110   .LF3FC
 3120   LD   A,&3F       ; Fetch CCP
 3130   CALL OSBYTE
 3140   
 3150   ; Initialise vectors
 3160   ; ------------------
 3170   .LF401
 3180   CALL &FFBF       ; Ensure RSTERR at &0038 is set up
 3190   LD   HL,(&FF84)  ; Get default error handler
 3200   LD   (&FFFA),HL  ; Set error handler to it
 3210   LD   A,&E5
 3220   LD   HL,&0001    ; ESC key returns —II
 3230   CALL OSBYTE
 3240   LD   A,&C3
 3250   LD   (&0000),A   ; Put JP opcode into
 3260   LD   (&0005),A   ; RESET and BDOS
 3270   .LF41A
 3280   LD   HL,BIOS_WBOOT ; Put RESET destination
 3290   LD   (&0001),HL    ; in address field
 3300   LD   HL,&E006      ; Put BDOS destination
 3310   LD   (&0006),HL    ; in address field
 3320   .DiskNull
 3330   RET
 3340   
 3350   
 3360   
 3370   .LF427
 3380   EX   (SP),HL
 3390   PUSH DE
 3400   PUSH BC
 3410   PUSH AF
 3420   .LF42B
 3430   LD   A,(HL)
 3440   INC  HL
 3450   AND  A
 3460   JR   Z,LF437
 3470   PUSH HL
 3480   CALL LF43C
 3490   POP  HL
 3500   JR   LF42B
 3510   .LF437
 3520   POP  AF
 3530   POP  BC
 3540   POP  DE
 3550   EX   (SP),HL
 3560   RET
 3570   
 3580   .LF43C
 3590   CP   &0D
 3600   JR   NZ,LF445
 3610   CALL LF445
 3620   LD   A,&0A
 3630   .LF445
 3640   LD   C,A
 3650   JP   ConsoleOut
 3660   
 3670   ; left-over code, a copy of the startup code and bits of BDOS
 3680   .LF449
 3690   RLCA
 3700   DJNZ LF444
 3710   LD   HL,&FCA3
 3720   JR   LF454
 3730   
 3740   .LF451
 3750   LD   HL,&FF8A
 3760   .LF454
 3770   LD   (HL),&FF
 3780   CALL LF401
 3790   CALL LF25E
 3800   CALL &E00C
 3810   LD   C,&00
 3820   JP   &D800
 3830   LD   SP,&322E
 3840   JR   NC,LF49A
 3850   ADD  A,B
 3860   NOP
 3870   EI
 3880   LD   A,&03
 3890   CALL OSWRCH
 3900   CALL &E00F
 3910   CALL LF3FC
 3920   LD   C,&00
 3930   JP   &D803
 3940   LD   A,&3F
 3950   CALL &28F4
 3960   .LF481
 3970   JR   LF481
 3980   INC  BC
 3990   JR   NZ,LF48D
 4000   LD   A,&8D
 4010   CALL OSBYTE
 4020   JR   LF49A
 4030   .LF48D
 4040   LD   L,&03
 4050   CP   &02
 4060   JR   Z,LF495
 4070   LD   L,&00
 4080   .LF495
 4090   LD   A,&8C
 4100   CALL OSBYTE
 4110   .LF49A
 4120   POP  HL
 4130   RET
 4140   LD   (&1364),SP
 4150   PUSH HL
 4160   LD   HL,(&FFFA)
 4170   LD   (&1362),HL
 4180   LD   HL,&06DB
 4190   LD   (&FFFA),HL
 4200   POP  HL
 4210   RET
 4220   LD   SP,(&1364)
 4230   POP  HL
 4240   LD   HL,(&FF82)
 4250   PUSH HL
 4260   INC  HL
 4270   SUB  A,A
 4280   LD   BC,&FFFF
 4290   CPIR
 4300   DEC  HL
 4310   LD   (HL),&0D
 4320   POP  HL
 4330   LD   A,(HL)
 4340   INC  HL
 4350   OR   A
 4360   JR   LF4C9
 4370   XOR  A
 4380   .LF4C9
 4390   PUSH HL
 4400   LD   HL,(&1362)
 4410   LD   (&FFFA),HL
 4420   POP  HL
 4430   RET
 4440   PUSH DE
 4450   XOR  A
 4460   LD   E,A
 4470   CALL OSARGS
 4480   POP  DE
 4490   RET
 4500   LD   A,&03
 4510   RET
 4520   .LF4DD:DEFB &4A
 4530   .LF4DE:DEFB &03
 4540   .LF4DF:DEF"SJ Econet ver 3.4Az$"
 4550   .LF4F3:DEFB &FE:DEFB &1E
 4560   .LF4F5:DEFB &CA:DEFB &B9:DEFB &0D
 4570   .LF4F8:DEFB &FE:DEFB &21
 4580   .LF4FA:DEFB &38:DEFB &02
 4590   .LF4FC:DEFB &D6:DEFB &09
 4600   .LF4FE:DEFB &D6:DEFB &0F
 4610 ]NEXT
 4620 OSCLI"Save ZNOS_BI "+STR$~mcode%+" "+STR$~O%+" "+STR$~load%+" "+STR$~load%
 4630 *Quit