First commit

This commit is contained in:
stid 2019-11-05 20:31:17 -08:00
commit 009a73ab89
8 changed files with 1893 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.vscode/*
bin/*
.DS_Store
*.o
*.sym
*.lst

747
arch/keyb.asm Executable file
View File

@ -0,0 +1,747 @@
BasicUpstart2(start)
// c64
.const bgcolor = $d020
.const bordercolor = $d021
.const screenram = $0400
// kernel functions
.const bsout = $ab1e //kernel character output sub
.const chrout = $ffd2
.const clearscreen = $e544
// chars
.const cr = 13
// memory map
.const kernel = $ff80
.const start_basic = $0281
.const end_basic = $0037
.const step_size_garb = $000f
.const dataset = $0090
.const input_dev = 153
.const out_dev = 154
.const time_1 = 160
.const time_2 = 161
.const time_3 = 162
.const ds_motor = 192
// color petscii
.const color_cyan = $9f
.const color_light_blue = $9a
.const color_green = $1e
// local const
.const pline_len = 40
.const hex_open_chr = $5b
.const hex_close_chr = $5d
.macro printHexByte(stringAddr, value) {
lda #<stringAddr
ldy #>stringAddr
ldx value
jsr printhex_row
}
.macro printHexWord(stringAddr, value) {
bPrint(stringAddr)
jsr popenhex
lda value+1
jsr printhex
lda value
jsr printhex
jsr pclosehex
bChar(cr)
}
.macro bPrint(stringAddr) {
lda #<stringAddr
ldy #>stringAddr
jsr bsout
}
.macro bChar(char) {
lda #char
jsr chrout
}
//==========================================================
// code
//==========================================================
start:
ldx #$00 // color
stx $d021 // set background color
stx $d020 // set border color
jsr clearscreen
bChar(color_cyan)
bPrint(str_about)
bChar(cr)
bChar(color_light_blue)
getKeyb:
jsr Keyboard
bcs NoValidInput
stx TempX
sty TempY
cmp #$ff
// Check A for Alphanumeric keys
beq NoNewAphanumericKey
// jsr printhex
cmp #'m'
beq mapMemory
cmp #'q'
beq exit
jmp getKeyb
NoNewAphanumericKey:
// Check X & Y for Non-Alphanumeric Keys
ldx TempX
ldy TempY
//stx $0401
//sty $0402
NoValidInput: // This may be substituted for an errorhandler if needed.
jmp getKeyb
exit: rts
mapMemory:
ldx #pline_len // lenht of pline
jsr pline
// ----------------------
// ------ map table start
// ----------------------
// kernel
printHexByte(str_row_kernel, kernel)
// start basic area
printHexWord(str_row_basic_start, start_basic)
// end basic area
printHexWord(str_row_basic_end, end_basic)
// garbadge
printHexByte(str_row_step_size_garb, printhex_row)
// dataset
printHexByte(str_row_dataset, dataset)
// input dev
printHexByte(str_row_input_dev, input_dev)
// out dev
printHexByte(str_row_out_dev, out_dev)
// time 1
printHexByte(str_row_time_1, time_1)
// time 2
printHexByte(str_row_time_2, time_2)
// time 2
printHexByte(str_row_time_2, time_3)
// dataset motor
printHexByte(str_row_ds_motor, ds_motor)
// ----------------------
// ------ map table end
// ----------------------
bChar(cr)
ldx #pline_len
jsr pline
rts
//
// popenhex
//
popenhex:
bChar(hex_open_chr)
bChar(color_green)
rts
//
// pclosehex
//
pclosehex:
bChar(color_light_blue)
bChar(hex_close_chr)
rts
//
// printhex_row
//
//
// preparatory ops: .a: msn ascii char
// .x: lsn ascii char
// .y: value
//
// returned values: none
//
printhex_row:
sta a_cache // preserve a
txa // store x using a as it will be lost after bsout
pha
lda a_cache // get back a
jsr bsout
jsr popenhex
pla // saved x back in a from saved a
jsr printhex
jsr pclosehex
bChar(cr)
rts
//
// printhex
//
//
// preparatory ops: .a: byte to convert
//
// returned values: none
//
printhex:
pha
jsr binhex
jsr chrout
txa
jsr chrout
pla
rts
//
// btohex
//
//
// preparatory ops: .a: byte to convert
//
// returned values: .a: msn ascii char
// .x: lsn ascii char
// .y: entry value
//
binhex: pha //save byte
and #%00001111 //extract lsn
tax //save it
pla //recover byte
lsr //extract...
lsr //msn
lsr
lsr
pha //save msn
txa //lsn
jsr binhex1 //generate ascii lsn
tax //save
pla //get msn & fall thru
//
// convert nybble to hex ascii equivalent...
binhex1: cmp #$0a
bcc binhex2 //in decimal range
adc #$66 //hex compensate
binhex2: eor #%00110000 //finalize nybble
rts //done
//
// pline
//
//
// preparatory ops: .x: lenght
// returned values: none
//
pline:
cpx #0
bne pline_start
ldx #12 // default to 12 if 0 is passed in x
pline_start: lda #102
pline_loop: jsr chrout
dex
cpx #0
bne pline_loop
lda #cr
jsr chrout
rts
.encoding "petscii_mixed"
str_about:
.text "c=64 mapper v2.0 by =stid="
.byte 13, 0
str_row_kernel:
.text " kernel "
.byte 0
str_row_basic_start:
.text " start basic area "
.byte 0
str_row_basic_end:
.text " end basic area "
.byte 0
str_row_step_size_garb:
.text " step size garbage "
.byte 0
str_row_dataset:
.text " dataset "
.byte 0
str_row_input_dev:
.text " input device "
.byte 0
str_row_out_dev:
.text " output device "
.byte 0
str_row_time_1:
.text " time 1 "
.byte 0
str_row_time_2:
.text " time 2 "
.byte 0
str_row_time_3:
.text " time 3 "
.byte 0
str_row_ds_motor:
.text " dataset motor "
.byte 0
a_cache:
.byte 0
TempX:
.byte 0
TempY:
.byte 0
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Keyboard IO Routine
~~~~~~~~~~~~~~~~~~~
By: TWW/CTR
Preparatory Settings
~~~~~~~~~~~~~~~~~~~~
None
Destroys
~~~~~~~~
Accumulator
X-Register
Y-Register
Carry / Zero / Negative
$dc00
$dc01
$50-$5f
Footprint
~~~~~~~~~
#$206 Bytes
Information
~~~~~~~~~~~
The routine uses "2 Key rollower" or up to 3 if the key-combination doesen't induce shadowing.
If 2 or 3 keys are pressed simultaneously (within 1 scan) a "No Activity" state has to occur before new valid keys are returned.
RESTORE is not detectable and must be handled by NMI IRQ.
SHIFT LOCK is not detected due to unreliability.
Usage
~~~~~
Example Code:
jsr Keyboard
bcs NoValidInput
stx TempX
sty TempY
cmp #$ff
beq NoNewAphanumericKey
// Check A for Alphanumeric keys
sta $0400
NoNewAphanumericKey:
// Check X & Y for Non-Alphanumeric Keys
ldx TempX
ldy TempY
stx $0401
sty $0402
NoValidInput: // This may be substituted for an errorhandler if needed.
Returned
~~~~~~~~
+=================================================+
| Returned in Accumulator |
+===========+===========+=============+===========+
| $00 - @ | $10 - p | $20 - SPC | $30 - 0 |
| $01 - a | $11 - q | $21 - | $31 - 1 |
| $02 - b | $12 - r | $22 - | $32 - 2 |
| $03 - c | $13 - s | $23 - | $33 - 3 |
| $04 - d | $14 - t | $24 - | $34 - 4 |
| $05 - e | $15 - u | $25 - | $35 - 5 |
| $06 - f | $16 - v | $26 - | $36 - 6 |
| $07 - g | $17 - w | $27 - | $37 - 7 |
| $08 - h | $18 - x | $28 - | $38 - 8 |
| $09 - i | $19 - y | $29 - | $39 - 9 |
| $0a - j | $1a - z | $2a - * | $3a - : |
| $0b - k | $1b - | $2b - + | $3b - ; |
| $0c - l | $1c - £ | $2c - , | $3c - |
| $0d - m | $1d - | $2d - - | $3d - = |
| $0e - n | $1e - ^ | $2e - . | $3e - |
| $0f - o | $1f - <- | $2f - / | $3f - |
+-----------+-----------+-------------+-----------+
+================================================================================
| Return in X-Register |
+=========+=========+=========+=========+=========+=========+=========+=========+
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
+---------+---------+---------+---------+---------+---------+---------+---------+
| CRSR UD | F5 | F3 | F1 | F7 | CRSR RL | RETURN |INST/DEL |
+---------+---------+---------+---------+---------+---------+---------+---------+
+================================================================================
| Return in Y-Register |
+=========+=========+=========+=========+=========+=========+=========+=========+
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
+---------+---------+---------+---------+---------+---------+---------+---------+
|RUN STOP | L-SHIFT | C= | R-SHIFT |CLR/HOME | CTRL | | |
+---------+---------+---------+---------+---------+---------+---------+---------+
CARRY:
- Set = Error Condition (Check A for code):
A = #$01 => No keyboard activity is detected.
A = #$02 => Control Port #1 Activity is detected.
A = #$03 => Key Shadowing / Ghosting is detected.
A = #$04 => 2 or 3 new keys is detected within one scan
A = #$05 => Awaiting "No Activity" state
- Clear = Valid input
A = #$ff => No new Alphanumeric Keys detected (some key(s) being held down AND/OR some Non-Alphanumeric key is causing valid return).
A <> #$ff => New Alphanumeric Key returned. Non-Alphanumeric keys may also be returned in X or Y Register
Issues/ToDo:
~~~~~~~~~~~~
- None
Improvements:
~~~~~~~~~~~~~
- Replace the subroutine with a pseudocommand and account for speedcode parameter (Memory vs. Cycles).
- Shorten the routine / Optimize if possible.
History:
~~~~~~~~
V2.5 - New test tool.
Added return of error codes.
Fixed a bug causing Buffer Overflow.
Fixed a bug in Non Alphanumerical Flags from 2.0.
V2.1 - Shortened the source by adding .for loops & Updated the header and some comments.
Added "simultaneous keypress" check.
V2.0 - Added return of non-Alphanumeric keys into X & Y-Registers.
Small optimizations here and there.
V1.1 - Unrolled code to make it faster and optimized other parts of it.
Removed SHIFT LOCK scanning.
V1.0 - First Working Version along with test tool.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.pc = * "Keyboard Scan Routine"
// ZERO PAGE Varibles
.const ScanResult = $50 // 8 bytes
.const BufferNew = $58 // 3 bytes
.const KeyQuantity = $5b // 1 byte
.const NonAlphaFlagX = $5c // 1 byte
.const NonAlphaFlagY = $5d // 1 byte
.const TempZP = $5e // 1 byte
.const SimultaneousKeys = $5f // 1 byte
// Operational Variables
.var MaxKeyRollover = 3
Keyboard:
{
jmp Main
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Routine for Scanning a Matrix Row
KeyInRow:
asl
bcs *+5
jsr KeyFound
.for (var i = 0 ; i < 7 ; i++) {
inx
asl
bcs *+5
jsr KeyFound
}
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Routine for handling: Key Found
KeyFound:
stx TempZP
dec KeyQuantity
bmi OverFlow
ldy KeyTable,x
ldx KeyQuantity
sty BufferNew,x
ldx TempZP
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Routine for handling: Overflow
OverFlow:
pla // Dirty hack to handle 2 layers of JSR
pla
pla
pla
// Don't manipulate last legal buffer as the routine will fix itself once it gets valid input again.
lda #$03
sec
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Exit Routine for: No Activity
NoActivityDetected:
// Exit With A = #$01, Carry Set & Reset BufferOld.
lda #$00
sta SimultaneousAlphanumericKeysFlag // Clear the too many keys flag once a "no activity" state is detected.
stx BufferOld
stx BufferOld+1
stx BufferOld+2
sec
lda #$01
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Exit Routine for Control Port Activity
ControlPort:
// Exit with A = #$02, Carry Set. Keep BufferOld to verify input after Control Port activity ceases
sec
lda #$02
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Configure Data Direction Registers
Main:
ldx #$ff
stx $dc02 // Port A - Output
ldy #$00
sty $dc03 // Port B - Input
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for Port Activity
sty $dc00 // Connect all Keyboard Rows
cpx $dc01
beq NoActivityDetected
lda SimultaneousAlphanumericKeysFlag
beq !+
// Waiting for all keys to be released before accepting new input.
lda #$05
sec
rts
!:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for Control Port #1 Activity
stx $dc00 // Disconnect all Keyboard Rows
cpx $dc01 // Only Control Port activity will be detected
bne ControlPort
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Scan Keyboard Matrix
lda #%11111110
sta $dc00
ldy $dc01
sty ScanResult+7
sec
.for (var i = 6 ; i > -1 ; i--) {
rol
sta $dc00
ldy $dc01
sty ScanResult+i
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for Control Port #1 Activity (again)
stx $dc00 // Disconnect all Keyboard Rows
cpx $dc01 // Only Control Port activity will be detected
bne ControlPort
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Initialize Buffer, Flags and Max Keys
// Reset current read buffer
stx BufferNew
stx BufferNew+1
stx BufferNew+2
// Reset Non-AlphaNumeric Flag
inx
stx NonAlphaFlagY
// Set max keys allowed before ignoring result
lda #MaxKeyRollover
sta KeyQuantity
// Counter to check for simultaneous alphanumeric key-presses
lda #$fe
sta SimultaneousKeys
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check and flag Non Alphanumeric Keys
lda ScanResult+6
eor #$ff
and #%10000000 // Left Shift
lsr
sta NonAlphaFlagY
lda ScanResult+0
eor #$ff
and #%10100100 // RUN STOP - C= - CTRL
ora NonAlphaFlagY
sta NonAlphaFlagY
lda ScanResult+1
eor #$ff
and #%00011000 // Right SHIFT - CLR HOME
ora NonAlphaFlagY
sta NonAlphaFlagY
lda ScanResult+7 // The rest
eor #$ff
sta NonAlphaFlagX
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for pressed key(s)
lda ScanResult+7
cmp #$ff
beq *+5
jsr KeyInRow
.for (var i = 6 ; i > -1 ; i--) {
ldx #[7-i]*8
lda ScanResult+i
beq *+5
jsr KeyInRow
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Key Scan Completed
// Put any new key (not in old scan) into buffer
ldx #MaxKeyRollover-1
!: lda BufferNew,x
cmp #$ff
beq Exist // Handle 'null' values
cmp BufferOld
beq Exist
cmp BufferOld+1
beq Exist
cmp BufferOld+2
beq Exist
// New Key Detected
inc BufferQuantity
ldy BufferQuantity
sta Buffer,y
// Keep track of how many new Alphanumeric keys are detected
inc SimultaneousKeys
beq TooManyNewKeys
Exist:
dex
bpl !-
// Anything in Buffer?
ldy BufferQuantity
bmi BufferEmpty
// Yes: Then return it and tidy up the buffer
dec BufferQuantity
lda Buffer
ldx Buffer+1
stx Buffer
ldx Buffer+2
stx Buffer+1
jmp Return
BufferEmpty: // No new Alphanumeric keys to handle.
lda #$ff
Return: // A is preset
clc
// Copy BufferNew to BufferOld
ldx BufferNew
stx BufferOld
ldx BufferNew+1
stx BufferOld+1
ldx BufferNew+2
stx BufferOld+2
// Handle Non Alphanumeric Keys
ldx NonAlphaFlagX
ldy NonAlphaFlagY
rts
TooManyNewKeys:
sec
lda #$ff
sta BufferQuantity
sta SimultaneousAlphanumericKeysFlag
lda #$04
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
KeyTable:
.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff // CRSR DOWN, F5, F3, F1, F7, CRSR RIGHT, RETURN, INST DEL
.byte $ff, $05, $13, $1a, $34, $01, $17, $33 // LEFT SHIFT, "E", "S", "Z", "4", "A", "W", "3"
.byte $18, $14, $06, $03, $36, $04, $12, $35 // "X", "T", "F", "C", "6", "D", "R", "5"
.byte $16, $15, $08, $02, $38, $07, $19, $37 // "V", "U", "H", "B", "8", "G", "Y", "7"
.byte $0e, $0f, $0b, $0d, $30, $0a, $09, $39 // "N", "O" (Oscar), "K", "M", "0" (Zero), "J", "I", "9"
.byte $2c, $00, $3a, $2e, $2d, $0c, $10, $2b // ",", "@", ":", ".", "-", "L", "P", "+"
.byte $2f, $1e, $3d, $ff, $ff, $3b, $2a, $1c // "/", "^", "=", RIGHT SHIFT, HOME, ";", "*", "£"
.byte $ff, $11, $ff, $20, $32, $ff, $1f, $31 // RUN STOP, "Q", "C=" (CMD), " " (SPC), "2", "CTRL", "<-", "1"
BufferOld:
.byte $ff, $ff, $ff
Buffer:
.byte $ff, $ff, $ff, $ff
BufferQuantity:
.byte $ff
SimultaneousAlphanumericKeysFlag:
.byte $00
}

801
arch/precursor.asm Executable file
View File

@ -0,0 +1,801 @@
BasicUpstart2(start)
// c64
.const bgcolor = $d020
.const bordercolor = $d021
.const screenram = $0400
// kernel functions
.const kBsout = $ab1e //kernel character output sub
.const kChrout = $ffd2
.const clearscreen = $e544
// chars
.const cr = 13
// memory map
.const kernel = $ff80
.const start_basic = $0281
.const end_basic = $0037
.const step_size_garb = $000f
.const dataset = $0090
.const input_dev = 153
.const out_dev = 154
.const time_1 = 160
.const time_2 = 161
.const time_3 = 162
.const ds_motor = 192
// color petscii
.const color_cyan = $9f
.const color_light_blue = $9a
.const color_green = $1e
// local const
.const pline_len = 40
.const hex_open_chr = $5b
.const hex_close_chr = $5d
// zero page
.const stringLow = $FB
.const stirngHigh = $FC
.const videoLow = $FD
.const videoHigh = $FE
.macro printHexByte(stringAddr, value) {
lda #<stringAddr
ldy #>stringAddr
ldx value
jsr printhex_row
}
.macro printHexWord(stringAddr, value) {
bPrint(stringAddr)
jsr popenhex
lda value+1
jsr printhex
lda value
jsr printhex
jsr pclosehex
bChar(cr)
}
.macro print(stringAddr) {
lda #<stringAddr
ldy #>stringAddr
jsr bsout
}
.macro bPrint(stringAddr) {
lda #<stringAddr
ldy #>stringAddr
jsr bsout
}
.macro bChar(char) {
lda #char
jsr kChrout
}
//==========================================================
// code
//==========================================================
start:
ldx #$00 // color
stx $d021 // set background color
stx $d020 // set border color
lda #$04
sta videoHigh
lda #$00
sta videoLow
jsr clearscreen
bChar(color_cyan)
print(str_about)
bChar(cr)
bChar(color_light_blue)
getKeyb:
jsr Keyboard
bcs NoValidInput
stx TempX
sty TempY
cmp #$ff
// Check A for Alphanumeric keys
beq NoNewAphanumericKey
// jsr printhex
cmp #'m'
beq mapMemory
cmp #'q'
beq exit
jmp getKeyb
NoNewAphanumericKey:
// Check X & Y for Non-Alphanumeric Keys
ldx TempX
ldy TempY
//stx $0401
//sty $0402
NoValidInput: // This may be substituted for an errorhandler if needed.
jmp getKeyb
exit: rts
bsout:
sty stirngHigh
sta stringLow
ldy #$00
bloop: lda (stringLow), y
cmp #0
beq blexit
sta (videoLow), y
iny
cpy #$f0
bne bcontinue
clc
lda videoLow
adc #$f0
sta videoLow
lda videoHigh
adc #$00
sta videoHigh
bcontinue: jmp bloop
blexit:
rts
mapMemory: {
ldx #pline_len // lenht of pline
jsr pline
// ----------------------
// ------ map table start
// ----------------------
// kernel
printHexByte(str_row_kernel, kernel)
// start basic area
printHexWord(str_row_basic_start, start_basic)
// end basic area
printHexWord(str_row_basic_end, end_basic)
// garbadge
printHexByte(str_row_step_size_garb, printhex_row)
// dataset
printHexByte(str_row_dataset, dataset)
// input dev
printHexByte(str_row_input_dev, input_dev)
// out dev
printHexByte(str_row_out_dev, out_dev)
// time 1
printHexByte(str_row_time_1, time_1)
// time 2
printHexByte(str_row_time_2, time_2)
// time 2
printHexByte(str_row_time_2, time_3)
// dataset motor
printHexByte(str_row_ds_motor, ds_motor)
// ----------------------
// ------ map table end
// ----------------------
bChar(cr)
ldx #pline_len
jsr pline
rts
}
//
// popenhex
//
popenhex:
bChar(hex_open_chr)
bChar(color_green)
rts
//
// pclosehex
//
pclosehex:
bChar(color_light_blue)
bChar(hex_close_chr)
rts
//
// printhex_row
//
//
// preparatory ops: .a: msn ascii char
// .x: lsn ascii char
// .y: value
//
// returned values: none
//
printhex_row:
sta a_cache // preserve a
txa // store x using a as it will be lost after ksout
pha
lda a_cache // get back a
jsr bsout
jsr popenhex
pla // saved x back in a from saved a
jsr printhex
jsr pclosehex
bChar(cr)
rts
//
// printhex
//
//
// preparatory ops: .a: byte to convert
//
// returned values: none
//
printhex:
pha
jsr binhex
jsr kChrout
txa
jsr kChrout
pla
rts
//
// btohex
//
//
// preparatory ops: .a: byte to convert
//
// returned values: .a: msn ascii char
// .x: lsn ascii char
// .y: entry value
//
binhex: pha //save byte
and #%00001111 //extract lsn
tax //save it
pla //recover byte
lsr //extract...
lsr //msn
lsr
lsr
pha //save msn
txa //lsn
jsr binhex1 //generate ascii lsn
tax //save
pla //get msn & fall thru
//
// convert nybble to hex ascii equivalent...
binhex1: cmp #$0a
bcc binhex2 //in decimal range
adc #$66 //hex compensate
binhex2: eor #%00110000 //finalize nybble
rts //done
//
// pline
//
//
// preparatory ops: .x: lenght
// returned values: none
//
pline:
cpx #0
bne pline_start
ldx #12 // default to 12 if 0 is passed in x
pline_start: lda #102
pline_loop: jsr kChrout
dex
cpx #0
bne pline_loop
lda #cr
jsr kChrout
rts
.encoding "screencode_mixed"
str_about:
.text "c=64 mapper v2.0 by =stid="
.byte 0
str_row_kernel:
.text " kernel "
.byte 0
str_row_basic_start:
.text " start basic area "
.byte 0
str_row_basic_end:
.text " end basic area "
.byte 0
str_row_step_size_garb:
.text " step size garbage "
.byte 0
str_row_dataset:
.text " dataset "
.byte 0
str_row_input_dev:
.text " input device "
.byte 0
str_row_out_dev:
.text " output device "
.byte 0
str_row_time_1:
.text " time 1 "
.byte 0
str_row_time_2:
.text " time 2 "
.byte 0
str_row_time_3:
.text " time 3 "
.byte 0
str_row_ds_motor:
.text " dataset motor "
.byte 0
str_row_generic:
.text " hex value "
.byte 0
a_cache:
.byte 0
TempX:
.byte 0
TempY:
.byte 0
cursorX:
.byte 0
cursorY:
.byte 0
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Keyboard IO Routine
~~~~~~~~~~~~~~~~~~~
By: TWW/CTR
Preparatory Settings
~~~~~~~~~~~~~~~~~~~~
None
Destroys
~~~~~~~~
Accumulator
X-Register
Y-Register
Carry / Zero / Negative
$dc00
$dc01
$50-$5f
Footprint
~~~~~~~~~
#$206 Bytes
Information
~~~~~~~~~~~
The routine uses "2 Key rollower" or up to 3 if the key-combination doesen't induce shadowing.
If 2 or 3 keys are pressed simultaneously (within 1 scan) a "No Activity" state has to occur before new valid keys are returned.
RESTORE is not detectable and must be handled by NMI IRQ.
SHIFT LOCK is not detected due to unreliability.
Usage
~~~~~
Example Code:
jsr Keyboard
bcs NoValidInput
stx TempX
sty TempY
cmp #$ff
beq NoNewAphanumericKey
// Check A for Alphanumeric keys
sta $0400
NoNewAphanumericKey:
// Check X & Y for Non-Alphanumeric Keys
ldx TempX
ldy TempY
stx $0401
sty $0402
NoValidInput: // This may be substituted for an errorhandler if needed.
Returned
~~~~~~~~
+=================================================+
| Returned in Accumulator |
+===========+===========+=============+===========+
| $00 - @ | $10 - p | $20 - SPC | $30 - 0 |
| $01 - a | $11 - q | $21 - | $31 - 1 |
| $02 - b | $12 - r | $22 - | $32 - 2 |
| $03 - c | $13 - s | $23 - | $33 - 3 |
| $04 - d | $14 - t | $24 - | $34 - 4 |
| $05 - e | $15 - u | $25 - | $35 - 5 |
| $06 - f | $16 - v | $26 - | $36 - 6 |
| $07 - g | $17 - w | $27 - | $37 - 7 |
| $08 - h | $18 - x | $28 - | $38 - 8 |
| $09 - i | $19 - y | $29 - | $39 - 9 |
| $0a - j | $1a - z | $2a - * | $3a - : |
| $0b - k | $1b - | $2b - + | $3b - ; |
| $0c - l | $1c - £ | $2c - , | $3c - |
| $0d - m | $1d - | $2d - - | $3d - = |
| $0e - n | $1e - ^ | $2e - . | $3e - |
| $0f - o | $1f - <- | $2f - / | $3f - |
+-----------+-----------+-------------+-----------+
+================================================================================
| Return in X-Register |
+=========+=========+=========+=========+=========+=========+=========+=========+
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
+---------+---------+---------+---------+---------+---------+---------+---------+
| CRSR UD | F5 | F3 | F1 | F7 | CRSR RL | RETURN |INST/DEL |
+---------+---------+---------+---------+---------+---------+---------+---------+
+================================================================================
| Return in Y-Register |
+=========+=========+=========+=========+=========+=========+=========+=========+
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
+---------+---------+---------+---------+---------+---------+---------+---------+
|RUN STOP | L-SHIFT | C= | R-SHIFT |CLR/HOME | CTRL | | |
+---------+---------+---------+---------+---------+---------+---------+---------+
CARRY:
- Set = Error Condition (Check A for code):
A = #$01 => No keyboard activity is detected.
A = #$02 => Control Port #1 Activity is detected.
A = #$03 => Key Shadowing / Ghosting is detected.
A = #$04 => 2 or 3 new keys is detected within one scan
A = #$05 => Awaiting "No Activity" state
- Clear = Valid input
A = #$ff => No new Alphanumeric Keys detected (some key(s) being held down AND/OR some Non-Alphanumeric key is causing valid return).
A <> #$ff => New Alphanumeric Key returned. Non-Alphanumeric keys may also be returned in X or Y Register
Issues/ToDo:
~~~~~~~~~~~~
- None
Improvements:
~~~~~~~~~~~~~
- Replace the subroutine with a pseudocommand and account for speedcode parameter (Memory vs. Cycles).
- Shorten the routine / Optimize if possible.
History:
~~~~~~~~
V2.5 - New test tool.
Added return of error codes.
Fixed a bug causing Buffer Overflow.
Fixed a bug in Non Alphanumerical Flags from 2.0.
V2.1 - Shortened the source by adding .for loops & Updated the header and some comments.
Added "simultaneous keypress" check.
V2.0 - Added return of non-Alphanumeric keys into X & Y-Registers.
Small optimizations here and there.
V1.1 - Unrolled code to make it faster and optimized other parts of it.
Removed SHIFT LOCK scanning.
V1.0 - First Working Version along with test tool.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
.pc = * "Keyboard Scan Routine"
// ZERO PAGE Varibles
.const ScanResult = $50 // 8 bytes
.const BufferNew = $58 // 3 bytes
.const KeyQuantity = $5b // 1 byte
.const NonAlphaFlagX = $5c // 1 byte
.const NonAlphaFlagY = $5d // 1 byte
.const TempZP = $5e // 1 byte
.const SimultaneousKeys = $5f // 1 byte
// Operational Variables
.var MaxKeyRollover = 3
Keyboard:
{
jmp Main
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Routine for Scanning a Matrix Row
KeyInRow:
asl
bcs *+5
jsr KeyFound
.for (var i = 0 ; i < 7 ; i++) {
inx
asl
bcs *+5
jsr KeyFound
}
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Routine for handling: Key Found
KeyFound:
stx TempZP
dec KeyQuantity
bmi OverFlow
ldy KeyTable,x
ldx KeyQuantity
sty BufferNew,x
ldx TempZP
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Routine for handling: Overflow
OverFlow:
pla // Dirty hack to handle 2 layers of JSR
pla
pla
pla
// Don't manipulate last legal buffer as the routine will fix itself once it gets valid input again.
lda #$03
sec
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Exit Routine for: No Activity
NoActivityDetected:
// Exit With A = #$01, Carry Set & Reset BufferOld.
lda #$00
sta SimultaneousAlphanumericKeysFlag // Clear the too many keys flag once a "no activity" state is detected.
stx BufferOld
stx BufferOld+1
stx BufferOld+2
sec
lda #$01
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Exit Routine for Control Port Activity
ControlPort:
// Exit with A = #$02, Carry Set. Keep BufferOld to verify input after Control Port activity ceases
sec
lda #$02
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Configure Data Direction Registers
Main:
ldx #$ff
stx $dc02 // Port A - Output
ldy #$00
sty $dc03 // Port B - Input
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for Port Activity
sty $dc00 // Connect all Keyboard Rows
cpx $dc01
beq NoActivityDetected
lda SimultaneousAlphanumericKeysFlag
beq !+
// Waiting for all keys to be released before accepting new input.
lda #$05
sec
rts
!:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for Control Port #1 Activity
stx $dc00 // Disconnect all Keyboard Rows
cpx $dc01 // Only Control Port activity will be detected
bne ControlPort
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Scan Keyboard Matrix
lda #%11111110
sta $dc00
ldy $dc01
sty ScanResult+7
sec
.for (var i = 6 ; i > -1 ; i--) {
rol
sta $dc00
ldy $dc01
sty ScanResult+i
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for Control Port #1 Activity (again)
stx $dc00 // Disconnect all Keyboard Rows
cpx $dc01 // Only Control Port activity will be detected
bne ControlPort
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Initialize Buffer, Flags and Max Keys
// Reset current read buffer
stx BufferNew
stx BufferNew+1
stx BufferNew+2
// Reset Non-AlphaNumeric Flag
inx
stx NonAlphaFlagY
// Set max keys allowed before ignoring result
lda #MaxKeyRollover
sta KeyQuantity
// Counter to check for simultaneous alphanumeric key-presses
lda #$fe
sta SimultaneousKeys
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check and flag Non Alphanumeric Keys
lda ScanResult+6
eor #$ff
and #%10000000 // Left Shift
lsr
sta NonAlphaFlagY
lda ScanResult+0
eor #$ff
and #%10100100 // RUN STOP - C= - CTRL
ora NonAlphaFlagY
sta NonAlphaFlagY
lda ScanResult+1
eor #$ff
and #%00011000 // Right SHIFT - CLR HOME
ora NonAlphaFlagY
sta NonAlphaFlagY
lda ScanResult+7 // The rest
eor #$ff
sta NonAlphaFlagX
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check for pressed key(s)
lda ScanResult+7
cmp #$ff
beq *+5
jsr KeyInRow
.for (var i = 6 ; i > -1 ; i--) {
ldx #[7-i]*8
lda ScanResult+i
beq *+5
jsr KeyInRow
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Key Scan Completed
// Put any new key (not in old scan) into buffer
ldx #MaxKeyRollover-1
!: lda BufferNew,x
cmp #$ff
beq Exist // Handle 'null' values
cmp BufferOld
beq Exist
cmp BufferOld+1
beq Exist
cmp BufferOld+2
beq Exist
// New Key Detected
inc BufferQuantity
ldy BufferQuantity
sta Buffer,y
// Keep track of how many new Alphanumeric keys are detected
inc SimultaneousKeys
beq TooManyNewKeys
Exist:
dex
bpl !-
// Anything in Buffer?
ldy BufferQuantity
bmi BufferEmpty
// Yes: Then return it and tidy up the buffer
dec BufferQuantity
lda Buffer
ldx Buffer+1
stx Buffer
ldx Buffer+2
stx Buffer+1
jmp Return
BufferEmpty: // No new Alphanumeric keys to handle.
lda #$ff
Return: // A is preset
clc
// Copy BufferNew to BufferOld
ldx BufferNew
stx BufferOld
ldx BufferNew+1
stx BufferOld+1
ldx BufferNew+2
stx BufferOld+2
// Handle Non Alphanumeric Keys
ldx NonAlphaFlagX
ldy NonAlphaFlagY
rts
TooManyNewKeys:
sec
lda #$ff
sta BufferQuantity
sta SimultaneousAlphanumericKeysFlag
lda #$04
rts
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
KeyTable:
.byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff // CRSR DOWN, F5, F3, F1, F7, CRSR RIGHT, RETURN, INST DEL
.byte $ff, $05, $13, $1a, $34, $01, $17, $33 // LEFT SHIFT, "E", "S", "Z", "4", "A", "W", "3"
.byte $18, $14, $06, $03, $36, $04, $12, $35 // "X", "T", "F", "C", "6", "D", "R", "5"
.byte $16, $15, $08, $02, $38, $07, $19, $37 // "V", "U", "H", "B", "8", "G", "Y", "7"
.byte $0e, $0f, $0b, $0d, $30, $0a, $09, $39 // "N", "O" (Oscar), "K", "M", "0" (Zero), "J", "I", "9"
.byte $2c, $00, $3a, $2e, $2d, $0c, $10, $2b // ",", "@", ":", ".", "-", "L", "P", "+"
.byte $2f, $1e, $3d, $ff, $ff, $3b, $2a, $1c // "/", "^", "=", RIGHT SHIFT, HOME, ";", "*", "£"
.byte $ff, $11, $ff, $20, $32, $ff, $1f, $31 // RUN STOP, "Q", "C=" (CMD), " " (SPC), "2", "CTRL", "<-", "1"
BufferOld:
.byte $ff, $ff, $ff
Buffer:
.byte $ff, $ff, $ff, $ff
BufferQuantity:
.byte $ff
SimultaneousAlphanumericKeysFlag:
.byte $00
}

35
hex.asm Normal file
View File

@ -0,0 +1,35 @@
#importonce
#import "mem_map.asm"
//
// btohex
//
//
// preparatory ops: .a: byte to convert
//
// returned values: .a: msn ascii char
// .x: lsn ascii char
// .y: entry value
//
binhex: pha //save byte
and #%00001111 //extract lsn
tax //save it
pla //recover byte
lsr //extract...
lsr //msn
lsr
lsr
pha //save msn
txa //lsn
jsr binhex1 //generate ascii lsn
tax //save
pla //get msn & fall thru
//
// convert nybble to hex ascii equivalent...
binhex1: cmp #$0a
bcc binhex2 //in decimal range
adc #$66 //hex compensate
binhex2: eor #%00110000 //finalize nybble
rts //done

74
main.asm Normal file
View File

@ -0,0 +1,74 @@
//BasicUpstart2(start)
* = $8000 "Main"
// Constants
.namespace constants {
.label MainColor = $05
.label BorderColor = $03
.label ScreenMem = $0400
}
.word coldstart // coldstart vector
.word start // coldstart vector
.byte $C3
.byte $C2
.byte $CD
.byte $38
.byte $30
coldstart:
sei
stx $d016
jsr $fda3 // Prepare IRQ
jsr $fd50 // Init memory. Rewrite this routine to speed up boot process.
jsr $fd15 // Init I/O
jsr $ff5b // Init video
cli
/*
jsr $E453
jsr $E3BF
jsr $E422
ldx #$FB
*/
//tsx
start:
jsr initApp;
print(testString)
.for(var i=0; i<23; i++) {
print(testString2)
}
deadLoop: jmp deadLoop
initApp: {
ClearColorRam($00)
ClearScreen(constants.ScreenMem, ' ')
SetBorderColor(constants.MainColor)
SetBackgroundColor(constants.BorderColor)
jsr Screen.init
rts
}
#import "screen.asm"
.encoding "screencode_mixed"
testString:
.text "fantastic job 1.0 by =stid="
.byte 13
.byte 13
.byte 0
testString2:
.text ".======================================."
.byte 13
.byte 0
* = $9FFF "EpromFiller"
.byte 0

29
math.asm Normal file
View File

@ -0,0 +1,29 @@
#importonce
.filenamespace Math
#import "mem_map.asm"
multiply: {
stx MemMap.MATH_SPACE.multiTmpX
pha
lda #$00
ldx #$08
clc
m0: bcc.r m1
clc
adc MemMap.MATH_SPACE.factor2
m1: ror
ror MemMap.MATH_SPACE.factor1
dex
bpl.r m0
ldx MemMap.MATH_SPACE.factor1
sta MemMap.MATH_SPACE.result
stx MemMap.MATH_SPACE.result+1
pla
ldx MemMap.MATH_SPACE.multiTmpX
rts
}

20
mem_map.asm Normal file
View File

@ -0,0 +1,20 @@
.filenamespace MemMap
#importonce
.const base = $10
.namespace SCREEN_SPACE {
.label TempVideoPointer = base // W
.label TempStringPointer = base+2 // W
.label CursorCol = base+4
.label CursorRow = base+5
.label tempY = base+6
}
.namespace MATH_SPACE {
.label factor1 = base+7
.label factor2 = base+8
.label multiTmpX = base+9
.label result = base+10 // W
}

181
screen.asm Normal file
View File

@ -0,0 +1,181 @@
#importonce
#import "math.asm"
#import "mem_map.asm"
// -----------------------
// MACROS
// -----------------------
.macro print(stringAddr) {
lda #<stringAddr // Low byte
ldx #>stringAddr // High byte
jsr Screen.print
}
.macro ClearScreen(screen, clearByte) {
lda #clearByte
ldx #0
!loop:
sta screen, x
sta screen + $100, x
sta screen + $200, x
sta screen + $300, x
inx
bne !loop-
}
.macro ClearColorRam(clearByte) {
lda #clearByte
ldx #0
!loop:
sta $D800, x
sta $D800 + $100, x
sta $D800 + $200, x
sta $D800 + $300, x
inx
bne !loop-
}
.macro SetBorderColor(color) {
lda #color
sta $d020
}
.macro SetBackgroundColor(color) {
lda #color
sta $d021
}
.macro SetMultiColor1(color) {
lda #color
sta $d022
}
.macro SetMultiColor2(color) {
lda #color
sta $d023
}
.macro SetMultiColorMode() {
lda $d016
ora #16
sta $d016
}
.macro SetScrollMode() {
lda $D016
eor #%00001000
sta $D016
}
.filenamespace Screen
// -----------------------
// CONSTANTS
// -----------------------
.namespace constants {
.label VIDEO_ADDR = $0400
.label COLUMN_NUM = 40
.label ROWS_NUM = 25
}
// -----------------------
// CODE
// -----------------------
init: {
lda #$00
sta MemMap.SCREEN_SPACE.CursorCol
sta MemMap.SCREEN_SPACE.CursorRow
rts
}
printChar: {
// New Line
cmp #13
bne.r !+
jsr screenNewLine
iny
rts
!:
// Store Base Video Address 16 bit
ldx #<constants.VIDEO_ADDR // Low byte
stx MemMap.SCREEN_SPACE.TempVideoPointer
ldx #>constants.VIDEO_ADDR // High byte
stx MemMap.SCREEN_SPACE.TempVideoPointer+1
// Temp Save Y
sty MemMap.SCREEN_SPACE.tempY
// CursorRow * 40
ldy MemMap.SCREEN_SPACE.CursorRow
sty MemMap.MATH_SPACE.factor1
ldy #constants.COLUMN_NUM
sty MemMap.MATH_SPACE.factor2
jsr Math.multiply
// Add mul result to TempVideoPointer
clc
pha
lda MemMap.MATH_SPACE.result
adc MemMap.SCREEN_SPACE.TempVideoPointer+1
sta MemMap.SCREEN_SPACE.TempVideoPointer+1
lda MemMap.MATH_SPACE.result+1
adc MemMap.SCREEN_SPACE.TempVideoPointer
sta MemMap.SCREEN_SPACE.TempVideoPointer
pla
// Add column
ldy MemMap.SCREEN_SPACE.CursorCol
sta (MemMap.SCREEN_SPACE.TempVideoPointer), y
ldy MemMap.SCREEN_SPACE.tempY
iny
inc MemMap.SCREEN_SPACE.CursorCol
lda MemMap.SCREEN_SPACE.CursorCol
cmp #constants.COLUMN_NUM+1
bcc exita
// CursorCol > COLUMN_NUM ? new line
jsr screenNewLine
exita:
rts
}
//
// print
//
//
// preparatory ops: .a: low byte string address
// .x: high byte string address
//
// returned values: none
//
print: {
ldy #$00
sta MemMap.SCREEN_SPACE.TempStringPointer
stx MemMap.SCREEN_SPACE.TempStringPointer+1
printLoop:
lda (MemMap.SCREEN_SPACE.TempStringPointer), y
cmp #0
beq exit
jsr Screen.printChar
jmp printLoop
exit:
rts
}
screenNewLine: {
lda #0
sta MemMap.SCREEN_SPACE.CursorCol
inc MemMap.SCREEN_SPACE.CursorRow
rts
}