Commodore 128 Tips & Tricks


Go to content

Assembler

This page contains some parts of code programming CPU 8502

.pc=$2000 "OVERCLOCKING" // approximatelly 1,3 MHz

start:
lda #$7f
sta $dc0d
sei
lda #<irq1
sta $0314
lda #>irq1
sta $0315

lda #$30
sta $d012
lda $d011
and #$7f
sta $d011

lda #$01
sta $d01a
sta $d019
cli
rts // jmp do_your_code

irq1:
lda $d019
sta $d019

lda #<irq2
sta $0314
lda #>irq2
sta $0315
lda #$fb
sta $d012

lda #$00
sta $d030
jmp $fa65 //$ff33

irq2:
lda $d019
sta $d019

lda #<irq1
sta $0314
lda #>irq1
sta $0315
lda #$30
sta $d012

lda #$01
sta $d030
jmp $fa65

8502 to Z80

1000 *= $8000 ; start on $8000 (change if necessary)
1010 .LIS N ; no listing
1020 .OBJ M ; object code in memory
1030 :
1040 : ; This part is essential, no errors allowed
1050 :
1060 LDA $FF00 ; MMU bank configuration register
1070 PHA ; save it on stack
1080 SEI ; disable the system interrupt
1090 LDA #$C3 ; store Z-80 opcode - JP
1100 STA $FFEE ; on boot-link address $FFEE .. $FFF0
1110 LDA #<Z80 ; lobyte Z-80 routine, defined below
1120 STA $FFEF
1130 LDA #>Z80 ; hibyte Z-80 routine
1140 STA $FFF0
1150 LDA #$3E ; set the configuration register
1160 STA $FF00 ; with the appropriate value
1170 LDA $D505 ; save the mode configuration register
1180 PHA ; for later
1190 LDA #$B0 ; and set the mode configuration
1200 STA $D505 ; to 'Z-80 active'
1210 : ; when this last instruction is executed
1220 : ; the Z-80 is active and the 8502 is frozen
1230 NOP ; IMPORTANT: give the 8502 time to start
1240 PLA ; now back in good ol' 8502 mode
1250 STA $D505 ; restore old mode configuration
1260 PLA ; and
1270 STA $FF00 ; old memory configuration
1280 CLI ; enable the system interrupt
1290 RTS ; end of essential subroutine
1300 :
1500 : ; Routine for the Z-80
1510 :
1520 Z80 ; Label for the Z-80 routine (s.a. above)
1530 .MOD 1 ; Z-80 opcodes now valid in assembler
1540 LD A, $3F ; change value of configuration register
1550 LD ($FF00), A ; to the appropriate value
1560 :
1570 : ; *** sample program
1580 : ; - clear VIC-II graphic screen
1590 :
1600 LD A, $00 ; Fill byte $00 on the first
1610 LD ($2000), A ; location of graphic screen ($2000)
1620 LD HL, $2000 ; load HL with address value $2000
1630 LD DE, $2001 ; load DE with address value $2001
1640 LD BC, 7999 ; load BC register with 7999
1650 : ; (number of bytes to fill)
1660 LDIR ; execute (HL) -> (DE), 7999 times
1670 :
1680 : ; *** end of sample program
1690 :
1700 : ; Crucial for the switch back
1710 :
1720 JP $FFE0 ; jump to bootlink routine in the Z-80 ROM
1730 : ; i.e. switch 8502 on, and Z-80 off



Revisions of ROMs:

I have a copy of an announcement from Commodore detailing the differences
between the rev. 0 and rev. 1 ROMs, with part numbers, signature bytes,
revision bytes, checksums and copyright dates. 252343-03 and 252343-04 fit
neither the rev. 0 or the rev. 1 descriptions, but are half way between
them. I have done a three way comparison of the ROMs. the results are
summarized in the following table.

rev. 0 252343-03 & 252343-04 rev. 1
copyright date 1985 1985 1986
ROM signature at 7FFC-D FFFF 5CAC 8DEF
rev. byte at 7FFE 00 01 01
checksum at 7FFF 4C DC 61
ROM signature at BFFC-D FFFF E266 CDC8
rev. byte at BFFE 00 01 01
checksum at 3A 8A 5C
ROM signature at CFFC-D FFFF AB16 8F76
rev. byte at CFFE 00 01 01
checksum at CFFF C3 BC 3C
rev. byte at FF80 00 01 01

All changes in 252343 are also present in the rev. 1 roms.
There are no changes in 252343 that are not in the rev. 1 ROMs.
There are additional changes in the rev. 1 ROMs which are not present in
252434.
There is one patch at FC44 that shows two stages of revision. The patch
in 252343 has five bytes added in the rev. 1 ROM at the begining of the
routine and the rest of the routine is move up in memory to make room for
it. Five addresses in the kernal are therefore different in all three
versions to accommodate the different versions of the patch.

These ROMs were probably a test version of the rev. 1 ROM since they
contain rev. 1 identification but do not conform to the announced rev. 1
checksums and signatures.

William M. Levak


Graphic allocation:

; Perform [graphic]

6B5A: C9 9C CMP #$9C
6B5C: D0 0B BNE $6B69
6B5E: 20 22 A0 JSR $A022 ; Move Basic to $1c01
6B61: 20 80 03 JSR $0380 ; CHRGET
6B64: A9 00 LDA #$00
6B66: 85 D8 STA $D8 ; Graphics mode code (BIT765: 000=0, 001=1, 011=2, 101=3, 111=4)
6B68: 60 RTS


pokedec("d8"),32:sysdec("6b5a")
$d8 = text/graphic mode flag

$9f4f - allocates RAM for GRAPHIC

$A022 - de-allocate RAM for GRAPHIC

$76 = 0 - not allocated / <> 0 - allocated

$80 = END
$A2 = NEW

IRQ on C128:

IRQ programming is quite different on the C128. Instead of the CIA generating the IRQ, the VIC-IIe generates the IRQ. To clear the main (raster) IRQ, you must write 1 into $D019.

The main IRQ sets up the 40-column display mode according to the value in $d8. (This is normally text mode, but will be bitmap mode if use BASIC's GRAPHIC n where n is 1 to 4). The main IRQ also calls KERNAL to scan keyboard and update jiffy clock (like the C64). The main IRQ also calls BASIC IRQ routine (unless a special bit is set to ignore BASIC IRQ).

Besides the main IRQ, the VIC-IIe can also generate other IRQs for BASIC. Specifically for use with sprites (COLLISION) and the light pen (PEN).

When IRQ occurs, you can test if it is the raster interrupt by

LDA $D019
AND #1
BEQ not_raster


Other bits in $D019 are set for sprite or light pen IRQ.

Besides that, the raster IRQ can happen twice if using split screen (GRAPHIC 2 or GRAPHIC 4). Only one of the two is the main IRQ.

To determine if the raster IRQ is the main IRQ, immediately check raster high bit like this

;we know it is raster interrupt
LDA $D011
BPL not_main_irq


You should only call keyboard scan and jiffy clock update on main IRQ (not the middle raster in split screen) or jiffy clock will be really wrong and keys will type too fast.

When an IRQ occurs, the CPU will save A, X, Y (like C64) but will also save contents of $FF00 (current memory configuration) before JMP ($314).

If you write your own IRQ, you can end by JMP $FF33. This will restore $FF00, A, X, and Y then do RTI.

The memory will configured to BANK 15 before JMP ($314) so your IRQ routine should be visible in BANK 15.

For C128, Old IRQ (as you say) is $FA65.

To enable your own IRQ routine, it is much like C64:

SEI
LDY #>ROUTTAB
LDX #<ROUTTAB
STX $314
STY $315
CLI
RTS



IRQ routine:
============

.var routtab = $2020

.pc=$2000 "RASTER IRQ"

lda #$0f
jsr $ff68
sei
ldy #>routtab
ldx #<routtab
stx $0314
sty $0315
cli
rts

.pc=$2020 "MAIN IRQ"

lda $d019
and #$01
beq no
lda $d011
bpl no
// my code
end: jmp $ff33
no: jmp $fa65

LOAD&RUN

.pc=$1300 "INIT"

lda #4
ldx #<filename2
ldy #>filename2
jsr $ffbd // setnam
lda #00
ldx #00
jsr $ff68 // setbnk
lda #00
ldx #08
ldy #00
jsr $ffba // setlfs
lda #00
ldx $2d
ldy $2e
jsr $ffd5 // load
jsr $5aa6 // run
rts

filename2:
.text "NAME" // LEN = 4


National ROMs:

31. The ROM location $CFF8 is reserved for national character
ROM checksums. This does not apply to US ROMs, which contain
$FF here. (new since last release).

32. The ROM location $CFF9 is now reserved for country codes.
The US ROMs contain $FF here. (new since last release).

33. The ROM location $CFFA and $CFFB (lo/hi) contain the
national character set signature. This does not apply to US
ROMs, which contain $FFFF here. (new since last release).

It works but I think later versions (1986)

BANK 12 - 15
$CFF8/$CFF9/$CFFA/$CFFB

INT 255/255/255/255
FIN 255/255/255/255 - char $1B/#27=Ä
FRE 17 /2 /255/255
GER 255/255/255/255 - char $00/#0=§
ITA 17 /6 /255/255
NOR 17 /5 /255/255
SWE 8 /4 /91 /188

DCRGER 255/0/243/145

BANK 14
FIN Ä = $D0D8 (53464)
=====
66
24
60
102
126
102
102
0

BANK14
GER § = $D000 (53248)
=====
60
66
56
36
28
66
60
0


PEEK&POKE
=========
CLEAR KEYBOARD BUFFER POKE 208,0
DISABLE LIST (only line numbers) POKE 774,139
ENABLE LIST POKE 774,81
DISABLE STOP/RESTORE POKE 808,103
ENABLE STOP/RESTORE POKE 808,110
DISABLE LOAD POKE 816,0
ENABLE LOAD POKE 816,108
CHANGE KEY REPEATS POKE 2594,X (X=64...No repeat X=225...all keys repeat X=0...some keys repeat(cursor,etc))
CHANGE PRINT COLOR POKE 241,(0-15)
CLEAR FUNCTION KEY DEFs POKE 4096,0
USE LOWER CASE WITH CHAR POKE 4588,216
USE UPPER CASE WITH CHAR POKE 4588,208
SWITCH TO LOWER CASE CHR$(14)
SWITCH TO UPPER CASE CHR$(142)
SWITCH FAST/SLOW(0=SLOW) POKE 53296,(0-1)
PEEK(53296)=252(SLOW - 1MHz)/253(FAST - 2MHz)
DISABLE SAVE POKE 818,1
ENABLE SAVE POKE 818,78

DETECT WHICH COMPUTER A PROGRAM IS ON
PEEK(65534)...
72= C64 (also C128 in C64 mode)
23= C128
255= C+4 (? 253)
114= VIC20
135= C65

DETECT SCREEN WIDTH
PEEK(215) (0=40 columns...128=80 columns)

Reset the computer then typing list poke 774,61:poke 775,255
2Mhz (works in both 64 and 128 mode) poke 53296,1
1Mhz (works in both 64 and 128 mode) poke 53296,0
Give F1-F8 same function as 64 mode poke 828,183
Reset (same as sys64738 on 64) sys65341

Clear 40 screen: SYS 27440

POKE 24,37 - disable line numbers
POKE 24,27 - enable line numbers

POKE 774,139:POKE 24,37 - disable list - only free lines

Prints READY. prompt: JSR $4D2A / SYS 19754

Perform ESC sequence in assembler:

- place $1B to $F0
- load accumulator with code of letter ($40 - $5A)
- call BSOUT ($FFD2)
for example:

LDA #$1B
STA $F0
LDA #$58 // X-key
JSR $FFD2


- this routine performs
ESC+X, it is switching cursor between 40 columns and 80 columns screen


Detokenizing BASIC text located in every part of memory

.var toscreen = $ffd2
.var detoken = $5149
.var digit = $8e32


.pc=$0c00 "Detoken"

detokenize:
lda beginlo
ldx beginhi
sta $fa
stx $fb
line:ldy #$00
rag:lda #$fa
ldx #$01
jsr $ff74
sta $0d00,y
iny
cmp #00
bne rag
cpy #05
bmi rag
lda $0d00
ldx $0d01
cmp #00
bne cont
cpx #00
beq end
cont:lda #$00
ldx #$0d
sta $fa
stx $fb
ldy #02

lda #146
jsr toscreen
lda $0d02
tax
iny
lda $0d03
jsr digit
iny
lda #32
jsr toscreen
ldy #04
lda #<$0d04
sta $61
iny
lda #>$0d04
sta $62
ldy #0
jsr detoken
lda #00
sta $ff00
lda #$0d
jsr toscreen
lda $0d00
ldx $0d01
sta $fa
stx $fb
jmp line
end:rts

beginlo:
.byte $01

beginhi:
.byte $1c




Back to content | Back to main menu