📄 biosmain.asm
字号:
;Chris Ward's 6502 System
;System BIOS
;16/1/2000
;To view properly, set tab size to 10
ACIA EQU $D000 ;6551 ACIA
VIA1 EQU $D100 ;6522 VIA #1
VIA2 EQU $D200 ;6522 VIA #2
LCD EQU $D300 ;LCD interface
RTC EQU $D400 ;DS1687 Real Time Clock
IDE EQU $D500 ;IDE interface
; VIA #1
ORG VIA1
PORT1B .ds 1
PORT1A .ds 1
DDR1B .ds 1
DDR1A .ds 1
; VIA #2
ORG VIA2
PORT2B .ds 1
PORT2A .ds 1
DDR2B .ds 1
DDR2A .ds 1
; connected hardware
LEDPORT EQU PORT1B ;LEDs on VIA #1 port B
LEDDDR EQU DDR1B
KEYPORT EQU PORT1A ;keypad on VIA #1 port A
KEYDDR EQU DDR1A
; *** Zero page variables
; ! These are set in stone and the addresses mustn't change because user !
; ! programs depend on them. Add new variables at the end. !
; [$00-$13] reserved for cc65 runtime library
; (but could be used by machine code programs)
ORG $14
;general-purpose temp variables for BIOS functions
ZTMP0 .ds 1
ZTMP1 .ds 1
ZTMP2 .ds 1
ZTMP3 .ds 1
ZTMP4 .ds 1
ZTMP5 .ds 1
ZTMP6 .ds 1
ZTMP7 .ds 1
ZTMP8 .ds 1
ZTMP9 .ds 1
ZTMP10 .ds 1
ZTMP11 .ds 1
ZTMP12 .ds 1
ZTMP13 .ds 1
ZTMP14 .ds 1
ZTMP15 .ds 1
;for keypad functions
KEYCOL .ds 1 ;[$24] used in scanning routine
KEYCOLN .ds 1 ;[$25] " " "
KEYROW .ds 1 ;[$26] " " "
KEYNEW .ds 1 ;[$27] flag indicates key has been pressed
KEYVAL .ds 1 ;[$28] value of last key pressed
;RS232 interface
SERRXF .ds 1 ;[$29] flag: 1 = new byte received
SERRX .ds 1 ;[$2A] value of byte received on serial port
;for LCD functions
MSGBASE .ds 2 ;[$2B-2C] address of message to print
;for IR functions
IRLAST .ds 1 ;[$2D]
IRLEN .ds 1 ;[$2E]
;for RTC/time functions
TI_TICK .ds 1 ;[$2F] clock ticks - 256 per second
TI_SEC .ds 1 ;[$30] time: seconds
TI_MIN .ds 1 ;[$31] minutes
TI_HOUR .ds 1 ;[$32] hours
TI_DAY .ds 1 ;[$33] date: day of week
TI_DATE .ds 1 ;[$34] date
TI_MON .ds 1 ;[$35] month
TI_YEAR .ds 1 ;[$36] year
TI_CENT .ds 1 ;[$37] century
;for IDE functions
IDEBUFP .ds 2 ;[$38-39] buffer pointer
IDEBUFP1 .ds 2 ;[$3A-3B] temp copy of IDEBUFP
IDELBA0 .ds 1 ;[$3C-3F] 28-bit LBA address (low byte first)
IDELBA1 .ds 1
IDELBA2 .ds 1
IDELBA3 .ds 1
IDELBAF .ds 1 ;[$40] flag: 1 = drive supports LBA
IDESIZE .ds 4 ;[$41-44] drive size in sectors
IDESPC .ds 2 ;[$45-46] sectors per cylinder, for CHS conversion
IDESPT .ds 1 ;[$47] sectors per track, for CHS conversion
;filesystem
DRIVES .ds 8 ;[$48-4F] available logical drives
; codes: 00=no drive
; 01=hard disk 1 partition 1
; ... 07=hard disk 1 partition 7
DRIVE .ds 1 ;[$50] selects drive number for file open, disk format etc.
FILENAME .ds 2 ;[$51-52] pointer to filename for file open
FILE .ds 1 ;[$53] logical file number for next filesystem call
;more stuff
KEYTIMER .ds 1 ;[$54] another variable for the keypad scanning routine
REGBANK .ds 6 ;[$55-5A] register bank for cc65
; *** Stack at $100-$1FF
; *** Buffers
ORG $200
IRBUF .ds 256 ;buffer for recording IR codes
IDEBUF0 .ds 512 ;buffers for data to/from IDE device
IDEBUF1 .ds 512
; *** More data
DRIVETAB .ds 128 ;drive table - 8 drives, 16 bytes per drive
FILETAB .ds 128 ;open files table - 4 files, 32 bytes per file
; ***** CODE *****
ORG $E000
#include "6551acia.asm"
#include "ds1687.asm"
#include "fs.asm"
#include "ide.asm"
#include "keypad.asm"
#include "lcd.asm"
#include "math.asm"
; *** IRQ service routine
IRQSRV PHA
LDA ASTS ;interrupt from ACIA?
BMI ASRV
LDA #$0C ;read RTC register C
STA RTCADDR
LDA RTCDATA
BPL IRQSRV9
;interrupt from RTC...
JSR KEYSCAN ;do keypad scan
INC TI_TICK ;increment clock ticks
BNE IRQSRV9
JSR GETTIME ;update time once per second
IRQSRV9 PLA
RTI
; *** ACIA interrupt service routine
ASRV AND #$08 ;byte received?
BEQ IRQSRV9
LDA ARXD ;get byte
STA SERRX ;store byte
INC SERRXF ;set flag
JMP IRQSRV9
; *** Entry point on reset
RESET NOP
NOP
NOP
NOP
JSR INIT ;do initialisation
JMP MENU
; *** Initialise devices and variables
INIT CLD
LDA #$00 ;clear key press flag
STA KEYNEW
STA KEYTIMER
LDA #$0F ;set DDR for keypad (iiiioooo)
STA KEYDDR
STA KEYPORT ;set keypad columns high
LDA #$FF ;set DDR for LEDs (oooooooo)
STA LEDDDR
LDA #$00 ;turn LEDs off
STA LEDPORT
LDA #$00 ;set DDR for VIA 2 port A (iiiiiiii)
STA DDR2A ; (bit 0 = infra red)
JSR AINIT ;setup ACIA
JSR RINIT ;setup Real Time Clock
JSR LINIT ;setup LCD display
JSR MEMTEST ;perform memory test
CLI ;enable interrupts
JSR IDEINIT ;initialise IDE
JSR IDEID ;get IDE drive ID
JSR FSINIT ;initialise filesystem
RTS
; *** Memory test
MEMMSG1 fcs "Memory test... "
.byte $00
MEMMSG2 fcs "OK"
.byte $00
MEMMSG3 fcs "fail at $"
.byte $00
MEMTEST LDX #$01 ;X=high byte
LDY #$FF ;Y=low byte
STX $01
LDA #0
STA $00
LDA #MEMMSG1 ;print message
STA MSGBASE
LDA #MEMMSG1/256
STA MSGBASE+1
JSR LCDSTR
MEMLOOP INY
BNE MEMTST1
INX
CPX #$80 ;reached $8000? no more RAM...
BNE MEMTST
LDA #$00 ;turn all LEDs off
STA LEDPORT
LDA #MEMMSG2 ;print 'OK'
STA MSGBASE
LDA #MEMMSG2/256
STA MSGBASE+1
JSR LCDSTR
RTS
MEMTST STX $01
STX LEDPORT ;display high byte on LEDs
MEMTST1 LDA #$55
STA ($00),Y
CMP ($00),Y
BNE MEMFAIL
LDA #$AA
STA ($00),Y
CMP ($00),Y
BNE MEMFAIL
JMP MEMLOOP
MEMFAIL STY $00 ;store low byte of fail address
LDA #MEMMSG3 ;print 'fail at $'
STA MSGBASE
LDA #MEMMSG3/256
STA MSGBASE+1
JSR LCDSTR
LDA $01 ;print high byte
JSR LCDHEX
LDA $00 ;print low byte
JSR LCDHEX
MEMFAI1 LDA $01 ;display high byte on leds
STA LEDPORT
JSR DLY00
LDA $00 ;display low byte on leds
STA LEDPORT
JSR DLY00
JSR KEYSCAN ;check for keypress
LDA KEYNEW
BEQ MEMFAI1
DEC KEYNEW
RTS
; *** Main menu
MENU JSR LCDCLR
LDA #MENUTXT1
STA MSGBASE
LDA #MENUTXT1/256
STA MSGBASE+1
JSR LCDSTR
MENU01 JSR GETKEY
CMP #1
BNE MENU02
JSR CLOCK
JMP MENU
MENU02 CMP #2
BNE MENU03
JSR IRREC
JMP MENU
MENU03 CMP #3
BNE MENU04
JMP TERMINAL
MENU04 CMP #4
BNE MENU05
JSR SLOAD
JMP MENU
MENU05 CMP #5
BNE MENU06
JMP HDMENU
MENU06 CMP #6
BNE MENU07
JSR SCROLLER
JMP MENU
MENU07 JMP MENU01
;40 chars: " "
MENUTXT1 fcs "1.Clock 2.IR rec 3.Terminal 4.SLOAD 5.HD"
.byte $00
; *** Hard disk menu
HDMENU JSR LCDCLR
LDA #HDMENUT
STA MSGBASE
LDA #HDMENUT/256
STA MSGBASE+1
JSR LCDSTR
HDMENU01 JSR GETKEY
CMP #1
BNE HDMENU02
JMP LBATEST
HDMENU02 CMP #2
BNE HDMENU03
JSR DLPARTAB
JMP HDMENU
HDMENU03 CMP #0
BNE HDMENU05
JMP MENU
HDMENU05 JMP HDMENU01
;40 chars: " "
HDMENUT fcs "1.LBA2CHS 2.DLPARTAB 0.Main"
.byte $00
; *** Clock mode
CLOCKTXT1 fcs "1.Set 0.Exit"
.byte $00
CLOCK LDY TI_SEC ;store current seconds
JSR LCDCLR
JSR LCDTIME ;display time
LDA #' '
JSR LCDCHAR
JSR LCDDATE ;display date
LDA #' ' ;print a couple of spaces
JSR LCDCHAR
JSR LCDCHAR
LDA #CLOCKTXT1
STA MSGBASE
LDA #CLOCKTXT1/256
STA MSGBASE+1
JSR LCDSTR ;show menu text
CLOCK01 LDA KEYNEW ;key pressed?
BNE CLOCK03 ; yes
TYA ; no
CMP TI_SEC ;has time changed?
BNE CLOCK ; yes - redisplay time
JMP CLOCK01 ; no
CLOCK03 LDA KEYVAL
DEC KEYNEW
CMP #1
BNE CLOCK04
JSR CLKSET
JMP CLOCK
CLOCK04 CMP #0
BNE CLOCK01
RTS
CLKSET JSR LCDCLR
LDA #$0B ;Register B
STA RTCADDR
LDA #$C3 ;SET, PIE, BCD, 24h, DSE
STA RTCDATA
LDA #2 ;hours - digit 1
JSR GETNUM
JSR LCDHEX1
ASL A
ASL A
ASL A
ASL A
STA ZTMP0
LDA #9 ;hours - digit 2
JSR GETNUM
JSR LCDHEX1
ORA ZTMP0
STA ZTMP0
LDA #$04 ;select hours register
STA RTCADDR
LDA ZTMP0 ;update hours
STA RTCDATA
LDA #':'
JSR LCDCHAR
LDA #5 ;minutes - digit 1
JSR GETNUM
JSR LCDHEX1
ASL A
ASL A
ASL A
ASL A
STA ZTMP0
LDA #9 ;minutes - digit 2
JSR GETNUM
JSR LCDHEX1
ORA ZTMP0
STA ZTMP0
LDA #$02 ;select minutes register
STA RTCADDR
LDA ZTMP0 ;update minutes
STA RTCDATA
LDA #':'
JSR LCDCHAR
LDA #5 ;seconds - digit 1
JSR GETNUM
JSR LCDHEX1
ASL A
ASL A
ASL A
ASL A
STA ZTMP0
LDA #9 ;seconds - digit 2
JSR GETNUM
JSR LCDHEX1
ORA ZTMP0
STA ZTMP0
LDA #$00 ;select seconds register
STA RTCADDR
LDA ZTMP0 ;update seconds
STA RTCDATA
LDA #' '
JSR LCDCHAR
LDA #3 ;date - digit 1
JSR GETNUM
JSR LCDHEX1
ASL A
ASL A
ASL A
ASL A
STA ZTMP0
LDA #9 ;date - digit 2
JSR GETNUM
JSR LCDHEX1
ORA ZTMP0
STA ZTMP0
LDA #$07 ;select date register
STA RTCADDR
LDA ZTMP0
STA RTCDATA
LDA #'/'
JSR LCDCHAR
LDA #1 ;month - digit 1
JSR GETNUM
JSR LCDHEX1
ASL A
ASL A
ASL A
ASL A
STA ZTMP0
LDA #9 ;month - digit 2
JSR GETNUM
JSR LCDHEX1
ORA ZTMP0
STA ZTMP0
LDA #$08 ;select month register
STA RTCADDR
LDA ZTMP0
STA RTCDATA
LDA #'/'
JSR LCDCHAR
LDA #9 ;century - digit 1
JSR GETNUM
JSR LCDHEX1
ASL A
ASL A
ASL A
ASL A
STA ZTMP0
LDA #9 ;century - digit 2
JSR GETNUM
JSR LCDHEX1
ORA ZTMP0
STA ZTMP0
LDA #$48 ;select century register
STA RTCADDR
LDA ZTMP0
STA RTCDATA
LDA #9 ;year - digit 1
JSR GETNUM
JSR LCDHEX1
ASL A
ASL A
ASL A
ASL A
STA ZTMP0
LDA #9 ;year - digit 2
JSR GETNUM
JSR LCDHEX1
ORA ZTMP0
STA ZTMP0
LDA #$09 ;select year register
STA RTCADDR
LDA ZTMP0
STA RTCDATA
LDA #$0B ;Register B
STA RTCADDR
LDA #$43 ;PIE, BCD, 24h, DSE
STA RTCDATA
RTS
; *** LCDTIME: Display time on LCD
; A,X,Y preserved
LCDTIME PHA
LDA TI_HOUR
JSR LCDHEX ;Display hours
LDA #':' ;Display a ':'
JSR LCDCHAR
LDA TI_MIN
JSR LCDHEX ;Display minutes
LDA #':' ;Display a ':'
JSR LCDCHAR
LDA TI_SEC
JSR LCDHEX ;Display seconds
PLA
RTS
; *** LCDDATE: Display date on LCD
; A,X,Y preserved
LCDDATE PHA
LDA TI_DATE
JSR LCDHEX
LDA #'/'
JSR LCDCHAR
LDA TI_MON
JSR LCDHEX
LDA #'/'
JSR LCDCHAR
LDA TI_CENT
JSR LCDHEX
LDA TI_YEAR
JSR LCDHEX
PLA
RTS
; *** Terminal mode
TERMINAL JSR LCDCLR
TERMINA0 LDA KEYNEW
BEQ TERMINA0
LDA KEYVAL
DEC KEYNEW
TAY
LDA HEXASCII,Y ;convert to ASCII
JSR LCDCHAR ;print value on the LCD
JSR TXBYTE ;transmit value on RS232 port
JMP TERMINA0
; *** SLOAD: Load program via serial connection
SLOAD JSR LCDCLR
LDA #$00 ;load address: $1000
STA ZTMP0
LDA #$10
STA ZTMP1
JSR RXBYTE ;get program size
STA ZTMP2
JSR RXBYTE
STA ZTMP3
JSR LCDHEX
LDA ZTMP2
JSR LCDHEX
BNE SLOAD0
DEC ZTMP3
SLOAD0 DEC ZTMP2
LDY #0
SLOAD1 JSR RXBYTE
STA (ZTMP0),Y
LDA ZTMP2
BNE SLOAD2
DEC ZTMP3
LDA ZTMP3
CMP #$FF
BEQ SLOAD3
LDA #'.'
JSR LCDCHAR
SLOAD2 DEC ZTMP2
INC ZTMP0
BNE SLOAD1
INC ZTMP1
JMP SLOAD1
SLOAD3 JSR LCDCLR
JMP $1000
; *** Record IR sequence
IRRECMSG fcs "IR Rec: "
.byte $00
IRREC JSR LCDCLR
LDA #IRRECMSG
STA MSGBASE
LDA #IRRECMSG/256
STA MSGBASE+1
JSR LCDSTR
IRREC1 LDA PORT2A
AND #$01
STA IRLAST
BNE IRREC1
LDY #$00 ;Y=pulse count
IRPULSE LDX #$00 ;X=pulse length
IRPULSE1 LDA PORT2A
AND #$01
CMP IRLAST
STA IRLAST
BNE IRPULSE2
INX
CPX #$FF ;maximum pulse length
BNE IRPULSE1
STY IRLEN
LDA #'L'
JSR LCDCHAR
LDA #' '
JSR LCDCHAR
LDA IRLEN
STA LEDPORT
JSR LCDHEX
JSR IRDUMP
JSR WAITKEY
RTS
IRPULSE2 TXA
STA IRBUF,Y
INY
BNE IRPULSE
LDA #'N'
JSR LCDCHAR
JSR WAITKEY
RTS
; *** Dump recorded IR sequnce to serial port
IRDUMP LDA IRLEN
JSR TXBYTE
LDY #$00
IRDUMP1 CPY IRLEN
BEQ IRDUMPX
LDA IRBUF,Y
JSR TXBYTE
INY
JMP IRDUMP1
IRDUMPX RTS
; *** Scroller
SCROLLER
JSR LCDCLR
LDA #0
STA ZTMP0
SCROLL00 LDX #40
LDY ZTMP0
SCROLL01 LDA SCROLTXT,Y
JSR LCDCHAR
DEX
BEQ SCROLL02
INY
CPY #40
BNE SCROLL01
LDY #0
JMP SCROLL01
SCROLL02 LDA KEYNEW
BNE SCROLL04
LDA TI_TICK
CLC
ADC #$40
SCROLL03 CMP TI_TICK
BNE SCROLL03
INC ZTMP0
LDA ZTMP0
CMP #40
BNE SCROLL00
LDA #0
STA ZTMP0
JMP SCROLL00
SCROLL04 DEC KEYNEW
RTS
;40 chars: " "
SCROLTXT fcs " *** Merry Christmas *** "
; *** Ugly old delay routines (these will probably disappear soon)
DLY00 LDX #$00
LDY #$00
DLY00A DEY
BNE DLY00A
DEX
BNE DLY00A
RTS
DLY01 LDY #$00
DLY01A DEY
BNE DLY01A
RTS
; *** Lookup table for HEX to ASCII
HEXASCII fcs "0123456789ABCDEF"
; *** Lookup table for powers of 2
POW2 .byte $01, $02, $04, $08, $10, $20, $40, $80
; *** Jump table for calling BIOS routines from user programs
; ! MAKE SURE THESE DON'T CHANGE ADDRESS - ADD NEW ENTRIES AT END !
ORG $FF00
_LCDCLR JMP LCDCLR
_LCDCHAR JMP LCDCHAR
_LCDHEX JMP LCDHEX
_LCDHEX1 JMP LCDHEX1
_LCDSTR JMP LCDSTR
_LCDSTRN JMP LCDSTRN
_LCDGETXY JMP LCDGETXY
_LCDSETXY JMP LCDSETXY
_KEYSCAN JMP KEYSCAN
_WAITKEY JMP WAITKEY
_GETKEY JMP GETKEY
_GETNUM JMP GETNUM
_GETHEX JMP GETHEX
_TXBYTE JMP TXBYTE
_RXBYTE JMP RXBYTE
_IDERSEC JMP IDERSEC
_IDEWSEC JMP IDEWSEC
; *** Reset, NMI & IRQ vectors
ORG $FFFA
.word IRQSRV ;FIXME: should be NMI service routine
.word RESET
.word IRQSRV
.end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -