                        org         0xF000

DEF_KBOfRAM:     equ 0x30
DEF_SysFeatures: equ 0x80

                        dw          0xAA55
                        dw          0x1000

biosStart:              move        sp,0x0100

                ; Initialize interrupt vectors
                        xor         pa,pa
                        move        a,vec_none

                        move        vt,pa
                        move        word [pa],vec_divError
                        move        word [pa+2],vec_breakpoint
                        move        word [pa+4],vec_invOpcode
                        move        word [pa+6],vec_coproErr
                        move        word [pa+8],vec_sysCall
						move		word [pa+10],vec_invAddx
                        move        word [pa+12],vec_Fx
						move		word [pa+14],vec_Fx
						move		word [pa+16],vec_Fx
                    {{
                        add         pa,18
                        move        cl,16-9
    fillLoop:           move        [pa],a
                        ldax        pa,[pa+2]
                        iter        cl,fillLoop
                    }}
                        move        word [pa],vec_irq0
                        move        word [pa+2],vec_irq1
                        move        word [pa+4],vec_irq2
                        move        word [pa+6],vec_irq3
                    {{
                        add         pa,8
                        move        cl,12
    fillLoop:           move        [pa],a
                        ldax        pa,[pa+2]
                        iter        cl,fillLoop
                    }}

                ; Initialize serial interface and print startup message
						move		pd,0xC009
                        move        [pd],bl       ; Set DTR
                        move        [pd+1],a     ; Disable IRQ, clear R3

						move		pb,pa
						move		pa,str_startup
						call		printString
						move		pa,pb

                ; Clear out RAM
                        xor         a,a
                    {{
    fillLoop:           move        [pa],a
                        ldax        pa,[pa+2]
                        cmp         pa,0xC000
                        jl          fillLoop
                    }}

                ; Initialize PIC: Enable everything
                        xor         a,a
                        move        b,1
                        move        byte [pa],0x0F

                ; Initialize PIT: Clear it to 0
                        move        [pa+2],a

                ; Set up the system configuration registers
                        move        byte [pa+5],DEF_KBOfRAM
                        move        byte [pa+6],DEF_SysFeatures

                ; Initialize FDC1
                        move        [pa+0x12],b     ; Turn motor on

                ; Initialize FDC2
                        move        [pa+0x18],a     ; Clear SSR1/SSR2
                        move        [pa+0x1A],a     ; Turn motor off
                        move        [pa+0x0E],a     ; Disable interrupt

                ; Load first sector of diskette
                        ; Recalib.
                        move        a,0x000A
                        sysc
                        jc          bootError

                        move        a,0x000C
                        xor         b,b
                        xor         c,c
                        move        pa,0x0100
                        sysc
                        jc          bootError

                ; Jump into bootloader
						movx		b:a,b
						movx		d:c,a
						xor			pa,pa
						xor			pb,pb
						xor			pc,pc
						xor			pd,pd
						and			fl,0x02
						or			f,0x0102
                        jump        0x100

                ; We couldn't boot; let the user know and freeze.
bootError:              move        pa,str_noBoot
                        call        printString
						and			f,0x00FF
						halt

;------------------------------------------------------------------------------

; printString: Prints the ASCIZ string whose first character is [pa].
printString:            push        a
                        push        b
                        move        a,0x0007
                    {{
    loop:               move        bl,[pa]
                        cmp         bl,0
                        je          done
                        sysc
                        ldax        pa,[pa+1]
                        jump        loop
    done:           }}

                        pop         b
                        pop         a
                        retn

                ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

vec_divError:           move        pa,str_badVector
                        jump        dumpAndDie

vec_breakpoint:         move        pa,str_breakpoint
                        jump        dumpAndDie

vec_invOpcode:          move        pa,str_invOpcode
                        jump        dumpAndDie

vec_coproErr:           move        pa,str_coproErr
                        jump        dumpAndDie

vec_invAddx:			move		pa,str_invAddx
						jump		dumpAndDie

vec_Fx:                 move        pa,str_Fx
                        jump        dumpAndDie

    ; IRQ0: Timer interrupt
vec_irq0:               push        pd
                        move        pd,0xC002
                        move        word [pd],0x0000     ; ACK the IRQ
                        jump        irq_iret

    ; IRQ1: Serial interface interrupt
vec_irq1:               push        pd
                        move        pd,0xC00A
                        move        byte [pd],0x0000     ; ACK/disable the IRQ
                        jump        irq_iret

    ; IRQ2: Primary FDC interrupt
vec_irq2:               push        pd
                        move        pd,0xC016
                        move        byte [pd],0x00       ; ACK/disable the IRQ
                        jump        irq_iret

    ; IRQ3: Secondary FDC interrupt
vec_irq3:               push        pd
                        move        pd,0xC01E
                        move        byte [pd],0x00      ; ACK/disable the IRQ
irq_iret:               pop         d
                        pop         f
                        retn

vec_none:               move        pa,str_badVector
dumpAndDie:             call        printString
                        move        pa,str_rebooting
                        call        printString
reset_system:           move        pa,0xC007
                        move        byte [pa],1
                        and			f,0x00FF
						halt

vec_sysCall:        {{
                        push        d
                        push        pd
                        cmp         al,0x0E
                        jg          invalidSysCall
                        movx        d,al
                        move        pd,jumpTable
                        ldax        pd,[pd+d*2]
                        jump        [pd]

    jumpTable:          dw          reset_system,func01,func02,func03
                        dw          func04,func05,func06,func07
                        dw          func08,func09,func0A,func0B
                        dw          func0C,func0D,func0E

    invalidSysCall:     or          byte [sp+4],4 ; Set FL.CF for return
    iret:               pop         pd
                        pop         d
                        pop         f
                        retn

    ; Set interrupt vector
    ; In:  AL = 0x01
    ;      AH = vector number
    ;      PA = handler address
    ; Out: PA = old handler address
    func01:             push        pc
                        move        pd,vt
                        movx        d,ah
                        and         d,0x1F
                        move        pc,[pd+d*2]
                        move        [pd+d*2],pa
                        move        pa,pc
                        pop         pc
                        jump        iret

    ; Get interrupt vector
    ; In:  AL = 0x02
    ;      AH = vector number
    ; Out: PA = handler address
    func02:             move        pd,vt
                        movx        d,ah
                        and         d,0x1F
                        move        pa,[pd+d*2]
                        jump        iret

    ; Set timeout for PIT
    ; In:  AL = 0x03
    ;      B  = count
    func03:             move        pd,0xC002
                        move        [pd],b
                        jump        iret

    ; Get memory size
    ; In:  AL = 0x04
    ; Out:  A = KB of system RAM
    func04:             move        a,DEF_KBOfRAM
                        jump        iret

    ; Get system features
    ; In:  AL = 0x05
    ; Out: AL = system feature bits
    func05:             move        al,DEF_SysFeatures
                        jump        iret

    ; Get serial port status
    ; In:  AL = 0x06
    ;      AH = serial port number
    ; Out: AL = port status bits
    func06:             cmp         ah,0
                {{
                        jne         noPort
                        move        pd,0xC008+1
                        move        dl,[pd]
                        move        dh,dl
                        move        al,dl

                        and         al,2
                        and         dl,4
                        and         dh,8
                        shl         dl,1
                        shr         dh,1
                        or          al,1
                        or          al,dl
                        or          al,dh
                        jump        iret

        noPort:         xor         al,al
                        jump        iret
                }}

    ; Write to serial port
    ; In:  AL = 0x07
    ;      AH = serial port number
    ;      BL = byte to send
    ; Out: FL.CF set if there was an error
    func07:             cmp         ah,0
                        jne         noSerialPort
                {{
                        move        pd,0xC008           ; Set DTROUT
                        move        byte [pd+1],1

                        test        byte [pd+1],2       ; If DTRIN is low,
                        jz          noTerminal          ;  there's no terminal

                        ; Wait for CTSIN & clear line, and make sure DTRIN
                        ; stays high
                        move        dh,0
        waitReady:      move        dl,[pd+1]
                        test        dl,2
                        jz          noTerminal
                        and         dl,0x1A
                        cmp         dl,0x1A
                        je          ready
                        iter        dh,waitReady
                        jump        serialTimeout

        ready:          move        [pd],bl
                        and         byte [sp+4],~4
                        jump        iret
                }}

    ; Read from serial port
    ; In:  AL = 0x08
    ;      AH = serial port number
    ; Out: FL.CF set on error; else AL = byte read
    func08:             cmp         ah,0
                        jne         noSerialPort
                {{
                        move        pd,0xC008           ; Set DTROUT
                        move        byte [pd+1],1

                        test        byte [pd+1],2       ; If DTRIN is low,
                        jz          noTerminal          ;  there's no terminal

                        ; Wait for a byte to be ready and make sure DTRIN stays
                        ; high.
                        move        dh,0
        waitReady:      move        dl,[pd+1]
                        test        dl,2
                        jz          noTerminal
                        and         dl,0x06
                        cmp         dl,0x06
                        je          ready
                        iter        dh,waitReady
                        jump        serialTimeout

        ready:          move        al,[pd]
                        and         byte [sp+4],~4
                        jump        iret
                }}

    noDrive:
    noSerialPort:       move        al,0x01
                        jump        invalidSysCall

    noMedium:
    noTerminal:         move        al,0x02
                        jump        invalidSysCall

    seekError:
    serialTimeout:      move        al,0x03
                        jump        invalidSysCall

    driveError:         move        al,0x04
                        jump        invalidSysCall

	FDC_SSR1:			equ			0
	FDC_SSR2:			equ			1
	FDC_DSR:			equ			2
	FDC_MSR:			equ			3
	FDC_CMD:			equ			4
	FDC_DATA:			equ			5
	FDC_ICR:			equ			6
    ; Get floppy drive status
    ; In:  AL = 0x09
    ;      AH = Drive number
    ; Out: AL = status byte
    func09:     {{
                        cmp         ah,1
                        jg          ret0
                        call        getDriveAddx
                        jz          ret0

                        move        d,[pd+FDC_DSR]
                        and         dl,0x06
                        move        al,dl
                        or          al,0x01
                        test        dh,0x01
                        jz          notBusy
                        or          al,0x08
        notBusy:        jump        iret

        ret0:           move        al,0
                        jump        iret
                }}

    ; Recalibrate floppy drive
    ; In:  AL = 0x0A
    ;      AH = Drive number
    ; Out: FL.CF set if there was an error
    func0A:             cmp         ah,1
                        jg          noDrive
                        call        getDriveAddx
                        jz          noDrive

                        move        byte [pd+FDC_DSR],1
                        move        d,[pd+FDC_DSR]
                        test        dl,0x02
                        jz          noMedium

                        xor         d,d
                        move        word [pd+FDC_SSR1],d
                        move        word [pd+FDC_ICR],d
                        move        byte [pd+FDC_CMD],dl
                {{
        waitReady:      test        byte [pd+FDC_MSR],0x01
                        jnz         waitReady
                }}
                        jump        diskReturn

    ; Seek drive head
    ; In:  AL = 0x0B
    ;      AH = Drive number
    ;      BL = Track
    ; Out: FL.CF set if error
    func0B:             cmp         ah,1
                        jg          noDrive
                        call        getDriveAddx
                        jz          noDrive

                        move        byte [pd+FDC_DSR],1
                        move        d,[pd+FDC_DSR]
                        test        dl,0x02
                        jz          noMedium

                        cmp         bl,39
                        jg          badParams
                        xor         d,d
                        move        [pd+FDC_SSR1],bl
                        move        byte [pd+FDC_SSR2],dl
                        move        word [pd+FDC_ICR],d
                        move        byte [pd+FDC_CMD],0x30
                {{
        waitReady:      test        byte [pd+FDC_MSR],0x01
                        jnz         waitReady
                }}
                        jump        diskReturn

    ; Read sector from floppy drive
    ; In:  AL = 0x0C
    ;      AH = Drive number
    ;      BL = Track
    ;      BH = Sector
    ;      CL = Head
    ;      PA = Out buffer base
    ; Out: FL.CF set if error
    func0C:             cmp         ah,1
                        jg          noDrive_2
                        call        getDriveAddx
                        jz          noDrive_2
                {{
                        move        byte [pd+FDC_DSR],1
                        move        d,[pd+FDC_DSR]
                        test        dl,0x02
                        jz          noMedium_2

                        call        checkParams
                        move        [pd+FDC_SSR1],bl
                        move        dl,bh
                        test        cl,cl
                        jz          head0
                        or          dl,0x80
        head0:          move        byte [pd+FDC_SSR2],dl

                        xor         d,d
                        move        word [pd+FDC_ICR],d
                        move        byte [pd+FDC_CMD],0x10
        waitReady:      test        byte [pd+FDC_MSR],0x01
                        jnz         waitReady

                        cmp         byte [pd+FDC_MSR],0
                        jnz         diskReturn

                        ; Buffer in 512 bytes
						push		a
                        move        a,0x100
        readLoop:       move        dl,[pd+FDC_DATA]
                        move        dh,[pd+FDC_DATA]
                        move        [pa],d
                        ldax        pa,[pa+2]
                        iter        a,readLoop
						sub			pa,512
						pop			a
                }}
                        and         byte [sp+4],~4
                        jump        iret

    noDrive_2:          jump        noDrive
    noMedium_2:         jump        noMedium
    seekError_2:        jump        seekError
    driveError_2:       jump        driveError

    ; Write sector to floppy drive
    ; In:  AL = 0x0D
    ;      AH = Drive number
    ;      BL = Track
    ;      BH = Sector
    ;      CL = Head
    ;      PA = Input buffer base
    ; Out: FL.CF set if error
    func0D:             cmp         ah,1
                        jg          noDrive_2
                        call        getDriveAddx
                        jz          noDrive_2
                {{
                        move        byte [pd+FDC_DSR],1
                        move        d,[pd+FDC_DSR]
                        test        dl,0x02
                        jz          noMedium_2

                        call        checkParams
                        move        [pd+FDC_SSR1],bl
                        move        dl,bh
                        test        cl,cl
                        jz          head0
                        or          dl,0x80
        head0:          move        byte [pd+FDC_SSR2],dl

                        xor         d,d
                        move        word [pd+FDC_ICR],d
                        move        byte [pd+FDC_CMD],0x20
        waitReady:      test        byte [pd+FDC_MSR],0x01
                        jnz         waitReady

                        cmp         byte [pd+FDC_MSR],0
                        jnz         diskReturn

                        ; Buffer out 512 bytes
						push		a
                        move        a,0x100
        writeLoop:      move        d,[pa]
                        move        [pd+FDC_DATA],dl
                        move        [pd+FDC_DATA],dh
                        ldax        pa,[pa+2]
                        iter        a,writeLoop
						sub			pa,512
						pop			a
                }}
                        and         byte [sp+4],~4
                        jump        iret

    ; Format track of floppy drive
    ; In:  AL = 0x0D
    ;      AH = drive number
    ;      BL = track number
    ; Out: FL.CF set if error
    func0E:             cmp         ah,1
                        jg          noDrive_2
                        call        getDriveAddx
                        jz          noDrive_2
                        move        byte [pd+FDC_DSR],1
                        move        d,[pd+FDC_DSR]
                        test        dl,0x02
                        jz          noMedium_2

                        cmp         bl,39
                        jg          badParams
                        xor         d,d
                        move        [pd+FDC_SSR1],bl
                        move        byte [pd+FDC_SSR2],dl
                        move        word [pd+FDC_ICR],d
                        move        byte [pd+FDC_CMD],0x30
                {{
        waitReady:      test        byte [pd+FDC_MSR],0x01
                        jnz         waitReady
                }}

    ; Handle return stuff
    diskReturn: {{
                        move        byte [pd+FDC_DSR],0
                        test        byte [pd+FDC_MSR],0x9E
                        jz          success
                        test        byte [pd+FDC_MSR],0x0E
                        jnz         skipSeekError
                        jump        seekError
        skipSeekError:  test        byte [pd+FDC_MSR],0x80
                        jz          driveErrorJump
                        test        byte [pd+FDC_DSR],0x02
                        jnz         skipNM
        driveErrorJump: jump        driveError
        skipNM:         jump        noMedium

        success:        and         byte [sp+4],~4
                        jump        iret
                }}

    ; Set PD to the base address of the drive's device I/O space
    getDriveAddx:       move        pd,0xC010
                {{
                        test        ah,ah
                        jz          drive0
                        ldax        pd,[pd+8]
        drive0:         test        byte [pd+FDC_DSR],0x80
                        retn
                }}

    ; Check drive parameters
    checkParams:        cmp         bl,39
                        jg          badParams
                        cmp         bh,8
                        jg          badParams
                        cmp         cl,1
                        jg          badParams
                        retn

    badParams:          move        al,0
                        jump        invalidSysCall
                    }};syscall

NUL: equ 0
BEL: equ 7
LF:  equ 10
FF:  equ 12
CR:  equ 13
str_startup:            db          BEL,FF,"Viejo 20 BIOS",CR,LF
                        db          "    Version 1.0",CR,LF,NUL
str_divErr:             db          BEL,"Divide error",NUL
str_breakpoint:         db          BEL,"Unhandled breakpoint",NUL
str_invOpcode:          db          BEL,"Invalid opcode",NUL
str_coproErr:           db          BEL,"Coprocessor error",NUL
str_invAddx:			db			BEL,"Invalid address generated",NUL
str_Fx:                 db          BEL,"Unhandled emulation trap",NUL
str_badVector:          db          BEL,"Invalid vector called",NUL
str_rebooting:          db          CR,LF,"Resetting system...",CR,LF,NUL
str_noBoot:             db          BEL,"*** Unable to load from primary "
                        db          "floppy disk ***",CR,LF
                        db          "System halted",CR,LF,NUL

                        org         0xFFF0
systemReset:            jump        biosStart
