@nataliapc/mcp-openmsx
Version:
Model context protocol server for openMSX automation and control
795 lines (731 loc) • 76.2 kB
Markdown
# 7. Machine Code Programs
This chapter contains a number of machine code programs to illustrate the use of MSX system resources. Although prepared with the ZEN Assembler they are designed t o run from BASIC and if necessary, may be entered in hex form using the loader shown below. The code should then be saved on cassette before any attempt is made to run it.
```
10 CLEAR 200,&HE000
20 ADDR=&HE000
30 PRINT RIGHT$ ("000"+HEX$(ADDR),4);
40 INPUT D$
50 POKE ADDR,VAL("&H"+D$)
60 ADDR=ADDR+l
70 GOTO 30
```
All the programs start at address E000H and are entered at the same point. Unless stated otherwise no parameter need be passed to a program, execution may therefore be initiated with a simple `DEFUSR=&HE000:?USR(0)` statement.
<a name="keyboard_matrix"></a>
## Keyboard Matrix
This program displays the keyboard matrix on the screen so that key depressions may be directly observed. The program may be terminated by pressing the CTRL and STOP keys. Note that spurious key depressions can be produced under certain circumstances if more than three or four keys are pressed at one time. This is a characteristic of all matrix type keyboards.
```
ORG 0E000H
LOAD 0E000H
; ******************************
; * BIOS STANDARD ROUTINES *
; ******************************
INITXT: EQU 006CH
CHPUT: EQU 00A2H
SNSMAT: EQU 0141H
BREAKX: EQU 00B7H
; ******************************
; * WORKSPACE VARIABLES *
; ******************************
INTFLG: EQU 0FC9BH
; ******************************
; * CONTROL CHARACTERS *
; ******************************
LF: EQU 10
HOME: EQU 11
CR: EQU 13
E000 CD6C00 MATRIX: CALL INITXT ; SCREEN 0
E003 3E0B MX1: LD A,HOME ;
E005 CDA200 CALL CHPUT ; Home Cursor
E008 AF XOR A ; A=KBD row
E009 F5 MX2: PUSH AF ;
E00A CD4101 CALL SNSMAT ; Read a row
E00D 0608 LD B,6 ; Eight cols
E00F 07 MX3: RLCA ; Select col
E010 F5 PUSH AF ;
E011 E601 AND 1 ;
E013 C630 ADD A,"0" ; Result
E015 CDA200 CALL CHPUT ; Display col
E018 F1 POP AF ;
E019 10F4 DJNZ MX3 ;
E01B 3E0D LD A,CR ; Newline
E01D CDA200 CALL CHPUT ;
E020 3E0A LD A,LF ;
E022 CDA200 CALL CHPUT ;
E025 F1 POP AF ; A=KBD row
E026 3C INC A ; Next row
E027 FE0B CP 11 ; Finished?
E029 20DE JR NZ,MX2 ;
E02B CDB700 CALL BREAKX ; CTRL-STOP
E02E 30D3 JR NC,MX1 ; Continue
E030 AF XOR A ;
E031 329BFC LD (INTFLG),A ; Clear possible STOP
E034 C9 RET ; Back to BASIC
END
```
<a name="40_column_graphics_text"></a>
## 40 Column Graphics Text
This program prints text on the [Graphics Mode](#graphics_mode) screen at forty characters per line. The string to be displayed is passed as the `USR` call parameter, for example `A$=USR("something")`. There us no need to open a GRP file beforehand, the only requirement of the program is that the screen be in the correct mode. The heart of the program is functionally equivalent to the [GRPPRT](#grpprt) standard routine but only the first six dot columns of a given character pattern are placed on the screen instead of eight. As the [GRPPRT](#grpprt) the pattern is placed at the current graphics position and the only control character recognised is ASCII CR (13) which functions as a combined CR, LF. Unlike the [GRPPRT](#grpprt) standard routine characters printed at negative coordinates, but which overlap the screen, will be correctly displayed. The program is currently set up to perform an auto linefeed after dot column 239, thus giving exactly forty characters per line. If required this may be changed, via the constant in the RMDCOL subroutine, so that the full width of the screen is usable.
```
ORG 0E000H
LOAD 0E000H
; ******************************
; * BIOS STANDARD ROUTINES *
; ******************************
RDSLT: EQU 000CH
CNVCHR: EQU 00ABH
MAPXYC: EQU 0111H
SETC: EQU 0120H
; ******************************
; * WORKSPACE VARIABLES *
; ******************************
FORCLR: EQU 0F3E9H
ATRBYT: EQU 0F3F2H
CGPNT: EQU 0F91FH
PATWRK: EQU 0FC40H
SCRMOD: EQU 0FCAFH
GRPACX: EQU 0FCB7H
GRPACY: EQU 0FCB9H
; ******************************
; * CONTROL CHARACTERS *
; ******************************
CR: EQU 13
E000 FE03 GFORTY: CP 3 ; String type?
E002 C0 RET NZ ;
E003 3AAFFC LD A,(SCRMOD) ; Mode
E006 FE02 CP 2 ; Graphics?
E008 C0 RET NZ ;
E009 EB EX DE,HL ; HL->Descriptor
E00A 46 LD B,(HL) ; B=String len
E00B 23 INC HL ;
E00C 5E LD E,(HL) ; Address LSB
E00D 23 INC HL ;
E00E 56 LD D,(HL) ; DE->String
E00F 04 INC B ;
E010 05 GF2: DEC B ; Finished?
E011 C8 RET Z ;
E012 1A LD A,(DE) ; A=Chr from string
E013 CD19E0 CALL GPRINT ; Print it
E016 13 INC DE ;
E017 18F7 JR GF2 ; Next chr
E019 F5 GPRINT: PUSH AF ;
E01A C5 PUSH BC ;
E01B D5 PUSH DE ;
E01C E5 PUSH HL ;
E01D FDE5 PUSH IY ;
E01F ED4BB7FC LD BC,(GRPACX) ; BC=X coord
E023 ED5BB9FC LD DE,(GRPACY) ; DE=Y coord
E027 CD39E0 CALL GDC ; Decode chr
E02A ED43B7FC LD (GRPACX),BC ; New X coord
E02E ED53B9FC LD (GRPACY),DE ; New Y coord
E032 FDE1 POP IY ;
E034 E1 POP HL ;
E035 D1 POP DE ;
E036 C1 POP BC ;
E037 F1 POP AF ;
E038 C9 RET ;
E039 CDAB00 GDC: CALL CNVCHR ; Check graphic
E03C D0 RET NC ; NC=Header
E03D 2007 JR NZ,GD2 ; NZ=Converted
E03F FE0D CP CR ; Carriage Return?
E041 2873 JR Z,GCRLF ;
E043 FE20 CP 20H ; Other control?
E045 D8 RET C ; Ignore
E046 6F GD2: LD L,A ;
E047 2600 LD H,0 ; HL=Chr code
E049 29 ADD HL,HL ;
E04A 29 ADD HL,HL ;
E04B 29 ADD HL,HL ; HL=Chr*8
E04C C5 PUSH BC ; X coord
E04D D5 PUSH DE ; Y coord
E04E ED5B20F9 LD DE,(CGPNT+1) ; Character set
E052 19 ADD HL,DE ; HL->Pattern
E053 1140FC LD DE,PATWRK ; DE->Buffer
E056 0608 LD B,8 ; Eight byte pattern
E058 C5 GD3: PUSH BC ;
E059 D5 PUSH DE ;
E05A 3A1FF9 LD A,(CGPNT) ; Slot ID
E05D CD0C00 CALL RDSLT ; Get pattern
E060 FB EI ;
E061 D1 POP DE ;
E062 C1 POP BC ;
E063 12 LD (DE),A ; Put in buffer
E064 13 INC DE ;
E065 23 INC HL ;
E066 10F0 DJNZ GD3 ; Next
E068 D1 POP DE ;
E069 C1 POP BC ;
E06A 3AE9F3 LD A,(FORCLR) ; Current colour
E06D 32F2F3 LS (ATRBYT),A ; Set ink
E070 FD2140FC LD IY,PATWRK ; IY->Patterns
E074 D5 PUSH DE ;
E075 2608 LD H,8 ; Max dot rows
E077 CB7A GD4: BIT 7,D ; Pos Y coord?
E079 202A JR NZ,GD8 ;
E07B CDBFE0 CALL BMDROW ; Bottom most row?
E07E 382B JR C,GD9 ; C=Y too large
E080 C5 PUSH BC ;
E081 2E06 LD L,6 ; Max dot cols
E083 FD7E00 LD A,(IY+0) ; A=Pattern row
E086 CB78 GD5: BIT 7,B ; Pos X coord
E088 2015 JR NZ,GD6 ;
E08A CDC8E0 CALL RMDCOL ; Rightmost col?
E08D 3815 JR C,GD7 ; C=X too large
E08F CB7F BIT 7,A ; Pattern bit
E091 280C JR Z,GD6 ; Z=0 Pixel
E093 F5 PUSH AF ;
E094 D5 PUSH DE ;
E095 E5 PUSH HL ;
E096 CD1101 CALL MAPXYC ; Map coords
E099 CD2001 CALL SETC ; Set pixel
E09C E1 POP HL ;
E09D D1 POP DE ;
E09E F1 POP AF ;
E09F 07 GD6: RLCA ; Shift pattern
E0A0 03 INC BC ; X=X+1
E0A1 2D DEC L ; Finished dot cols?
E0A2 20E2 JR NZ,GD5 ;
E0A4 C1 GD7: POP BC ; Initial X coord
E0A5 FD23 GD8: INC IY ; Next pattern byte
E0A7 13 INC DE ; Y=Y+1
E0A8 25 DEC H ; Finished dot rows?
E0A9 20CC JR NZ,GD4 ;
E0AB D1 GD9: POP DE ; Initial Y coord
E0AC 210600 LD HL,6 ; Step
E0AF 09 ADD HL,BC ; X=X+6
E0B0 44 LD B,H ;
E0B1 4D LD C,L ; BC=New X coord
E0B2 CDC8E0 CALL RMDCOL ; Rightmost col?
E0B5 D0 RET NC ;
E0B6 010000 GCRLF: LD BC,0 ; X=0
E0B9 210800 LD HL,8 ;
E0BC 19 ADD HL,DE ;
E0BD EB EX DE,HL ; Y=Y+8
E0BE C9 RET ;
E0BF E5 BMDROW: PUSH HL ;
E0C0 21BF00 LD HL,191 ; Bottom dot row
E0C3 B7 OR A ;
E0C4 ED52 SBC HL,DE ; Check Y coord
E0C6 E1 POP HL ;
E0C7 C9 RET ; C=Below screen
E0C8 E5 RMDCOL: PUSH HL ;
E0C9 21EF00 LD HL,239 ; Rightmost dot col
E0CC B7 OR A ;
E0CD ED42 SBC HL,BC ; Check X coord
E0CF E1 POP HL ;
E0D0 C9 RET ; C=Beyond right
END
```
<a name="string_bubble_sort"></a>
## String Bubble Sort
This program will sort the contents os a string Array into ascending alphabetic order. The location of the Array is passed as the `USR` call parameter, for example `V=USR(VARPRT(A$(0)))`. There are no restrictions on the size of the Array or on its contents but it must only have one dimension. The program is based on the classic bubble sort algorithm where string pairs are compared and their positions swapped if the second is smaller than the first. a 250 element Array of randomly generated stringswill be sorted in approximately 2.5 seconds. The equivalent BASIC program takes over six minutes.
```
ORG 0E000H
LOAD 0E000H
E000 FE02 SORT: CP 2 ; Integer type?
E002 C0 RET NZ ;
E003 23 INC HL ; HL->DAC+1
E004 23 INC HL ; HL->DAC+2
E005 5E LD E,(HL) ; Address LSB
E006 23 INC HL ; HL->DAC+3
E007 56 LD D,(HL) ; Address MSB
E008 EB EX DE,HL ; HL->A$(0)
E009 E5 PUSH HL ;
E00A DDE1 POP IX ; IX->A$(0)
E00C DD7EF8 LD A,(IX-8) ; Array type
E00F FE03 CP 3 ; String Array?
E011 C0 RET NZ ;
E012 DD7EFD LD A,(IX-3) ; Dimension
E015 3D DEC A ; Single dimension?
E016 C0 RET NZ ;
E017 DD4EFE LD C,(IX-2) ;
E01A DD46FF LD B,(IX-1) ; BC=Element count
E01D C5 SR2: PUSH BC ;
E01E E5 PUSH HL ; HL->Dsc(N)
E01F 46 SR3: LD B,(HL) ; B=Len(N)
E020 23 INC HL ;
E021 5E LD E,(HL) ;
E022 23 INC HL ;
E023 E5 PUSH HL ;
E024 56 LD D,(HL) ; DE->String(N)
E025 23 INC HL ; HL->Dsc(N+1)
E026 4E LD C,(HL) ; C=Len(N+1)
E027 23 INC HL ;
E028 7E LD A,(HL) ;
E029 23 INC HL ;
E02A E5 PUSH HL ;
E02B 66 LD H,(HL) ;
E02C 6F LD L,A ; HL->String(N+1)
E02D EB EX DE,HL ; HL->(N),DE->(N+1)
E02E 04 INC B ;
E02F 0C INC C ;
E030 05 SR4: DEC B ; Remaining len(N)
E031 2B25 JR Z,NEXT ; Z=(N)<=(N+1)
E033 0D DEC C ; Remaining len(N+1)
E034 2808 JR Z,SWAP ; Z=(N+1)<(N)
E036 1A LD A,(DE) ; Chr from (N+1)
E037 BE CP (HL) ; Chr from (N)
E038 13 INC DE ;
E039 23 INC HL ;
E03A 28F4 JR Z,SR4 ; Same, continue
E03C 301A JR NC,NEXT ; NC=(N)<(N+1)
E03E E1 SWAP: POP HL ; HL->Dsc(N+1)
E03F D1 POP DE ; DE->Dsc(N)
E040 0603 LD B,3 ; Descriptor size
E042 1A SW2: LD A,(DE) ; Swap descriptors
E043 4E LD C,(HL) ;
E044 77 LD (HL),A ;
E045 79 LD A,C ;
E046 12 LD (DE),A ;
E047 1B DEC DE ;
E048 2B DEC HL ;
E049 10F7 DJNZ SW2 ;
E04B DDE5 PUSH IX ;
E04D E1 POP HL ; HL->A$(0)
E04E B7 OR A ;
E04F ED52 SBC HL,DE ; At Array start?
E051 3007 JR NC,NX2 ; NC=At start
E053 1B DEC DE ; Back up
E054 1B DEC DE ;
E055 EB EX DE,HL ; HL->Dsc(N-1_
E056 18C7 JR SR3 ; Go check again
E058 E1 NEXT: POP HL ; Lose junk
E059 E1 POP HL ;
E05A E1 NX2: POP HL ; HL->Dsc(N)
E05B C1 POP BC ; BC=Element count
E05C 23 INC HL ; Next descriptor
E05D 23 INC HL ;
E05E 23 INC HL ;
E05F 0B DEC BC ;
E060 78 LD A,B ;
E061 B1 OR C ; Finished?
E062 20B9 JR NZ,SR2 ;
E064 C9 RET ;
END
```
<a name="graphics_screen_dump"></a>
## Graphics Screen Dump
This program will dump the screen contents, in any mode, to the printer. When first activated via a `USR` call the program merely patches itself into the interrupt handler keyscan hook.
Once the program has installed itself it effectively becomes an extension of the interrupt handler and a screen dump may then be initiated from any part of the system simply by pressing the ESC key. If necessary the dump can be terminated by pressing the CTRL and STOP keys. An example of a [Graphics Mode](#graphics_mode) screen, in which all thirty-two sprites are active, is shown below:
The simplest method of generating a screen dump is to copy all the character codes from the Name Table to the printer. However this would only work in the two text modes, the sprites could not be displayed and the result would reflect the printer's internal character set rather than the VDP character set. The program therefore reproduces the screen as a 240/256x192 bit image on the printer in all modes, each point in the image being derived from the colour code of the corresponding point on the screen. No dot for colours 0 to 7 and a dot for colours 8 to 15.
The colour code for a given point is obtained by first examining the thirty-two sprites in sequence to determine whether any one overlaps it. If every sprite is transparent at the point then the character plane is examined. This is done by using the point coordinates to locate the corresponding entry in the Name Table then, via the character code, to isolate the relevant bit in the associated pattern. If the bit's colour code is found to be transparent the background plane colour is returned.
Note that the control code sequences used in the program are the Epson FX80 printer. These are marked in the listings in case another printer is to be used. One sequence is used to enter bit image mode at the start of a 240/256 byte line (each byte defines eight vertical dots) and one sequence is used to initiate a paper feed at the end of the line. The program is generally optimised for speed, rather than for minimal code, and takes about five seconds plus printer time to produce the 46,080/49,152 dots in the image.
```
ORG 0E000H
LOAD 0E000H
; ******************************
; * BIOS STANDARD ROUTINES *
; ******************************
RDVRM: EQU 004AH
CALATR: EQU 0087H
LPTOUT: EQU 00A5H
; ******************************
; * WORKSPACE VARIABLES *
; ******************************
T32COL: EQU 0F3BFH
GRPNAM: EQU 0F3C7H
GRPCOL: EQU 0F3C9H
GRPCGP: EQU 0F3CBH
MLTNAM: EQU 0F3D1H
MLTCGP: EQU 0F3D5H
RG1SAV: EQU 0F3E0H
RG7SAV: EQU 0F3E6H
NAMBAS: EQU 0F922H
CGPBAS: EQU 0F924H
PATBAS: EQU 0F926H
ATRBAS: EQU 0F928H
SCRMOD: EQU 0FCAFH
HKEYC: EQU 0FDCCH
; ******************************
; * CONTROL CHARACTERS *
; ******************************
CR: EQU 13
ESC: EQU 27
E000 3ACCFD ENTRY: LD A,(HKEYC) ; Hook
E003 FEC9 CP 0C9H ; Free to use?
E005 C0 RET NZ ;
E006 2112E0 LD HL,DUMP ; Where to go
E009 22CDFD LD (HKEYC+1),HL ; Redirect hook
E00C 3ECD LD A,0CDH ; CALL
E00E 32CCFD LD (HKEYC),A ;
E011 C9 RET ;
E012 FE3A DUMP: CP 3AH ; ESC key number?
E014 C0 RET NZ ;
E015 F5 PUSH AF ;
E016 C5 PUSH BC ;
E017 D5 PUSH DE ;
E018 E5 PUSH HL ;
E019 ED734FE2 LD (BRKSTK),SP ; For CTRL-STOP
E01D 0E00 LD C,0 ; C=Row
E01F 3AAFFC DU1: LD A,(SCRMOD) ; Mode
E022 B7 OR A ;
E023 21F000 LD HL,240 ; T40 Dots per row
E026 112B06 LD DE,6*256+40 ;
E029 2806 JR Z,DU2 ;
E02B 210001 LD HL,256 ; T32,GRP,MLT Dots
E02E 112008 LD DE,8*256+32 ;
E031 3E1B DU2: LD A,ESC ; ***** FX80 *****
E033 CD8DE0 CALL PRINT ; * *
E036 3E4B LD A,"K" ; * Bit mode *
E038 CD8DE0 CALL PRINT ; * *
E03B 7D LD A,L ; * Bytes LSB *
E03C CD8DE0 CALL PRINT ; * *
E03F 7C LD A,H ; * Bytes MSB *
E040 CD8DE0 CALL PRINT ; ****************
E043 0600 LD B,0 ; B=Column
E045 CD97E0 DU3: CALL CELL ; Do an 8x8 cell
E048 D5 PUSH DE ;
E049 C5 PUSH BC ;
E04A 2151E2 LD HL,CBUFF ; HL->Colours
E04D 42 LD B,D ; B=Dot cols (6 or 8)
E04E 110800 LD DE,8 ; CBUFF offset
E051 C5 DU4: PUSH BC ;
E052 E5 PUSH HL ;
E053 0608 LD B,8 ; B=Dot rows
E055 7E DU5: LD A,(HL) ; A=Colour code
E056 FE08 CP 8 ; Dark or light?
E058 3F CCF ; Light=Print dot
E059 CB11 RL C ; Build result
E05B 19 ADD HL,DE ; Next dot row
E05C 10F7 DJNZ DU5 ;
E05E 79 LD A,C ; 8 Vertical dots
E05F CD8DE0 CALL PRINT ;
E062 E1 POP HL ;
E063 C1 POP BC ;
E064 23 INC HL ; Next dot col
E065 10EA DJNZ DU4 ;
E067 C1 POP BC ;
E068 D1 POP DE ;
E069 04 INC B ; Next column
E06A 78 LD A,B ;
E06B BB CP E ; End of row?
E06C 20D7 JR NZ,DU3 ;
E06E 3E0D LD A,CR ; Head left
E070 CD8DE0 CALL PRINT ;
E073 3E1B LD A,ESC ; ***** FX80 *****
E075 CD8DE0 CALL PRINT ; * *
E078 3E4A LD A,"J" ; * Paper feed *
E07A CD8DE0 CALL PRINT ; * *
E07D 3E18 LD A,24 ; * 24/216= 1/9" *
E07F CD8DE0 CALL PRINT ; ****************
E082 0C INC C ; Next row
E083 79 LD A,C ;
E084 FE18 CP 24 ; Finished screen?
E086 2097 JR NZ,DU1 ;
E088 E1 DU6: POP HL ;
E089 D1 POP DE ;
E08A C1 POP BC ;
E08B F1 POP AF ;
E08C C9 RET ;
E08D CDA500 PRINT: CALL LPTOUT ; To printer
E090 D0 RET NC ; CTRL-STOP?
E091 ED7B4FE2 LD SP,(BRKSTK) ; Restore stack
E095 18F1 JR DU6 ; Terminate program
E097 C5 CELL: PUSH BC ;
E098 D5 PUSH DE ;
E099 E5 PUSH HL ;
E09A FDE5 PUSH IY ;
E09C 2151E2 LD HL,CBUFF ; For results
E09F 3E40 LD A,64 ;
E0A1 3600 CL1: LD (HL),0 ; Transparent
E0A3 23 INC HL ;
E0A4 3D DEC A ; Fill
E0A5 20FA JR NZ,CL1 ;
E0A7 3AAFFC LD A,(SCRMOD) ; Mode
E0AA B7 OR A ; T40?
E0AB F5 PUSH AF ;
E0AC C5 PUSH BC ;
E0AD C469E1 CALL NZ,SPRTES ; Sprites first
E0B0 C1 POP BC ;
E0B1 69 LD L,C ;
E0B2 2600 LD H,0 ; HL=Row
E0B4 29 ADD HL,HL ;
E0B5 29 ADD HL,HL ;
E0B6 29 ADD HL,HL ; HL=Row*8
E0B7 5D LD E,L ;
E0B8 54 LD D,H ; DE=Row*8
E0B9 29 ADD HL,HL ;
E0BA 29 ADD HL,HL ; HL=Row*32
E0BB F1 POP AF ; Mode
E0BC F5 PUSH AF ;
E0BD 2001 JR NZ,CL2 ; T40?
E0BF 19 ADD HL,DE ; HL=Row*40
E0C0 58 CL2: LD E,B ; DE=Column
E0C1 19 ADD HL,DE ;
E0C2 EB EX DE,HL ; DE=NAMTAB offset
E0C3 D602 SUB 2 ; Mode
E0C5 79 LD A,C ; A=Row
E0C6 010000 LD BC,0 ; BC=CGPTAB offset
E0C9 2A24F9 LD HL,(CGPBAS) ;
E0CC E5 PUSH HL ;
E0CD 2A22F9 LD HL,(NAMBAS) ;
E0D0 3819 JR C,CL4 ; C=T40 or T32
E0D2 200C JR NZ,CL3 ; NZ=MLT
E0D4 2ACBF3 LD HL,(GRPCGP) ; Else GRP
E0D7 E3 EX (SP),HL ;
E0D8 2AC7F3 LD HL,(GRPNAM) ;
E0DB E618 AND 18H ; Row MSBs
E0DD 47 LD B,A ; 1/3=2kB CGP offset
E0DE 180B JR CL4 ;
E0E0 2AD5F3 CL3: LD HL,(MLTCGP) ;
E0E3 E3 EX (SP),HL ;
E0E4 2AD1F3 LD HL,(MLTNAM) ;
E0E7 07 RLCA ; Row*2
E0E8 E606 AND 6 ;
E0EA 4F LD C,A ; 1/6=2B CGP offset
E0EB 19 CL4: ADD HL,DE ; HL->NAMTAB
E0EC CD4A00 CALL RDVRM ; Get chr code
E0EF 6F LD L,A ;
E0F0 2600 LD H,0 ; HL=Chr code
E0F2 29 ADD HL,HL ;
E0F3 29 ADD HL,HL ;
E0F4 29 ADD HL,HL ; HL=Chr*8
E0F5 09 ADD HL,BC ; GRP,MLT offsets
E0F6 EB EX DE,HL ; DE=CGPTAB offset
E0F7 FDE1 POP IY ; IY=CGPTAB base
E0F9 FD19 ADD IY,DE ; IY->Pattern
E0FB 2AC9F3 LD HL,(GRPCOL) ;
E0FE 19 ADD HL,DE ; HL->GRP colours
E0FF 0F RRCA ;
E100 0F RRCA ;
E101 0F RRCA ; Chr code/8
E102 E61F AND 1FH ;
E104 4F LD C,A ;
E105 0600 LD B,0 ;
E107 3AE6F3 LD A,(RG7SAV) ; T40 Colours
E10A 57 LD D,A ; D=T40 Colours
E10B E60F AND 0FH ;
E10D 5F LD E,A ; E=Background colour
E10E F1 POP AF ; Mode
E10F E5 PUSH HL ; STK->GRP Colours
E110 3D DEC A ;
E111 2008 JR NZ,CL5 ; Z=T32
E113 2ABFF3 LD HL,(T32COL) ;
E116 09 ADD HL,BC ; HL->T32 Colours
E117 CD4A00 CALL RDVRM ; Get T32 Colours
E11A 57 LD D,A ; D=T32 Colours
E11B 2151E2 CL5: LD HL,CBUFF ; Results
E11E 0608 LD B,8 ; Dot rows
E120 FDE5 CL6: PUSH IY ;
E122 E3 EX (SP),HL ; HL->Pattern
E123 CD4A00 CALL RDVRM ; Get pattern
E126 4F LD C,A ; C=Pattern
E127 E1 POP HL ;
E128 FD23 INC IY ; Next dot row
E12A 3AAFFC LD A,(SCRMOD) ; Mode
E12D D602 SUB 2 ;
E12F 3815 JR C,CL8 ; C=T40 or T32
E131 280C JR Z,CL7 ; Z=GRP
E133 51 LD D,C ; MLT Colours=Pattern
E134 0EF0 LD C,0F0H ; Dummy MLT pattern
E136 78 LD A,B ; Dot row
E137 FE05 CP 5 ; Cell halfway mark?
E139 280B JR Z,CL8 ;
E13B FD2B DEC IY ; Back up pattern
E13D 1807 JR CL8 ;
E13F E3 CL7: EX (SP),HL ; HL->GRP Colours
E140 CD4A00 CALL RDVRM ; Get colours
E143 57 LD D,A ; D=GRP Colours
E144 23 INC HL ; Next dot row
E145 E3 EX (SP),HL ; STK->GRP Colours
E146 C5 CL8: PUSH BC ;
E147 0608 LD B,8 ; Dot cols
E149 CB11 CL9: RL C ; Dot from pattern
E14B 34 INC (HL) ;
E14C 35 DEC (HL) ; Check CBUFF clear
E14D 200D JR NZ,CL12 ; NZ=Sprite above
E14F 7A LD A,D ; A=Colours
E150 3004 JR NC,CL10 ; NC=0 Pixel
E152 0F RRCA ;
E153 0F RRCA ;
E154 0F RRCA ;
E155 0F RRCA ; Select 1 colour
E156 E60F CL10: AND 0FH ;
E158 2001 JR NZ,CL11 ; Z=Transparent
E15A 7B LD A,E ; Use background
E15B 77 CL11: LD (HL),A ; Colour in CBUFF
E15C 23 CL12: INC HL ;
E15D 10EA DJNZ CL9 ; Next dot col
E15F C1 POP BC ;
E160 10BE DJNZ CL6 ; Next dot row
E162 E1 POP HL ;
E163 FDE1 POP IY ;
E165 E1 POP HL ;
E166 D1 POP DE ;
E167 C1 POP BC ;
E168 C9 RET ;
E169 78 SPRTES: LD A,B ; A=Column
E16A 07 RLCA ;
E16B 07 RLCA ;
E16C 07 RLCA ; A=X coord
E16D C607 ADD A,7 ; RH edge of cell
E16F 47 LD B,A ; B=X coord
E170 79 LD A,C ; A=Row
E171 07 RLCA ;
E172 07 RLCA ;
E173 07 RLCA ; A=Y coord
E174 C607 ADD A,7 ; Bottom of cell
E176 4F LD C,A ; C=Y coord
E177 AF XOR A ; Sprite number
E178 CD8700 SS1: CALL CALATR ; HL->Attributes
E17B 57 LD D,A ; D=Sprite number
E17C CD4A00 CALL RDVRM ; Get Sprite Y
E17F FED0 CP 208 ; Terminator?
E181 C8 RET Z ;
E182 D5 PUSH DE ;
E183 C5 PUSH BC ;
E184 CD8FE1 CALL SPRITE ; Do a sprite
E187 C1 POP BC ;
E188 F1 POP AF ;
E189 3C INC A ; Next sprite number
E18A FE20 CP 32 ; Done all?
E18C 20EA JR NZ,SS1 ;
E18E C9 RET ;
E18F 91 SPRITE: SUB C ; (SY-Y)
E190 2F CPL ; Make (Y-SY)
E191 FE27 CP 39 ; Possible overlap?
E193 D0 RET NC ;
E194 4F LD C,A ; C=(Y-SY)
E195 23 INC HL ;
E196 CD4A00 CALL RDVRM ; Get Sprite X
E199 5F LD E,A ;
E19A 78 LD A,B ; A=X coord
E19B 93 SUB E ;
E19C 5F LD E,A ; E=(X-SX)
E19D 9F SBC A,A ; Make 16 bit
E19E 57 LD D,A ; DE=(X-SX)
E19F 23 INC HL ;
E1A0 CD4A00 CALL RDVRM ; Get pattern#
E1A3 47 LD B,A ;
E1A4 23 INC HL ;
E1A5 CD4A00 CALL RDVRM ; Get EC & Colour
E1A8 CB7F BIT 7,A ; Early clock?
E1AA 2805 JR Z,SP1 ;
E1AC 212000 LD HL,32 ;
E1AF 19 ADD HL,DE ; Increase (X-SX)
E1B0 EB EX DE,HL ;
E1B1 14 SP1: INC D ;
E1B2 15 DEC D ; (X-SX)>255 or neg?
E1B3 C0 RET NZ ; NZ-Outside cell
E1B4 E60F AND 0FH ; Colour
E1B6 C8 RET Z ; Z=Transparent
E1B7 57 LD D,A ; D=Colour
E1B8 3AE0F3 LD A,(RG1SAV) ; Flags
E1BB DB4F BIT 1,A ; SIZE
E1BD 0F RRCA ; MAG
E1BE 3E08 LD A,8 ; Minimum size
E1C0 3001 JR NC,SP2 ;
E1C2 87 ADD A,A ; Double for MAG
E1C3 2800 SP2: JR Z,SP3 ;
E1C5 CB80 RES 0,B ; Change pattern#
E1C7 CB88 RES 1,B ;
E1C9 87 ADD A,A ; Double for SIZE
E1CA 6F SP3: LD L,A ; L=Sprite size
E1CB C606 ADD A,6 ; Allow cell size
E1CD B9 CP C ;
E1CE D8 RET C ; Sprite above
E1CF BB CP E ;
E1D0 D8 RET C ; Sprite to left
E1D1 79 LD A,C ;
E1D2 D607 SUB 7 ; (Y-SY) from top
E1D4 4F LD C,A ;
E1D5 7D LD A,L ; A=Sprite size
E1D6 2608 LD H,8 ; Max dot rows
E1D8 3800 JR C,SP5 ; C=Below cell top
E1DA 91 SUB C ; A=Dot row overlap
E1DB FE09 CP 9 ;
E1DD 3802 JR C,SP4 ;
E1DF 3E08 LD A,8 ;
E1E1 67 SP4: LD H,A ; H=Row overlap
E1E2 7B SP5: LD A,E ;
E1E3 D607 SUB 7 ; (X-SX) from cell LH
E1E5 5F LD E,A ;
E1E6 7D LD A,L ; A=Sprite size
E1E7 2E08 LD L,8 ; Max dot cols
E1E9 3808 JR C,SP7 ; C=Past cell LH
E1EB 93 SUB E ; A=Dot col overlap
E1EC FE09 CP 9 ;
E1EE 3802 JR C,SP6 ;
E1F0 3E08 LD A,8 ;
E1F2 6F SP6: LD L,A ; L=Col overlap
E1F3 FD2151E2 SP7: LD IY,CBUFF ; Results
E1F7 D5 SP8: PUSH DE ;
E1F8 CB79 BIT 7,C ; Reached sprite?
E1FA 2048 JR NZ,SP15 ;
E1FC E5 PUSH HL ;
E1FD FDE5 PUSH IY ;
E1FF CB7B SP9: BIT 7,E ; Reached sprite?
E201 2038 JR NZ,SP14 ;
E203 FD7E00 LD A,(IY+0) ; CBUFF
E206 B7 OR A ; Transparent?
E207 2032 JR NZ,SP14 ;
E209 C5 PUSH BC ;
E20A D5 PUSH DE ;
E20B E5 PUSH HL ;
E20C 3AE0F3 LD A,(RG1SAV) ; Flags
E10F 0F RRCA ; MAG
E210 3004 JR NC,SP10 ;
E212 CB39 SRL C ; (Y-SY)/2
E214 CB3B SRL E ; (X-SX)/2
E216 CB5B SP10: BIT 3,E ; (X-SX)>7?
E218 2804 JR Z,SP11 ;
E21A CB9B RES 3,E ; (X-SX)-8
E21C CBE1 SET 4,C ; (Y-SY)+16
E21E 68 SP11: LD L,B ;
E21F 2600 LD H,0 ; HL=Pattern#
E221 44 LD B,H ; BC=Y offset
E222 29 ADD HL,HL ;
E223 29 ADD HL,HL ;
E224 29 ADD HL,HL ; HL=Pattern*8
E225 09 ADD HL,BC ; Select dot row
E226 ED4B26F9 LD BC,(PATBAS) ;
E22A 09 ADD HL,BC ; HL->Pattern
E22B CD4A00 CALL RDVRM ; Get dot row
E22E 1C INC E ;
E22F 07 SP12: RLCA ; Select dot col
E230 1D DEC E ;
E231 20FC JR NZ,SP12 ;
E233 3003 JR NC,SP13 ; NC=0 Pixel
E235 FD7200 LD (IY+0),D ; Colour in CBUFF
E238 E1 SP13: POP HL ;
E239 D1 POP DE ;
E23A C1 POP BC ;
E23B FD23 SP14: INC IY ;
E23D 1C INC E ; Right a dot col
E23E 2D DEC L ; Finished cols?
E23F 20BE JR NZ,SP9 ;
E241 FDE1 POP IY ;
E243 E1 POP HL ;
E244 110800 SP15: LD DE,8 ;
E247 FD19 ADD IY,DE ;
E249 D1 POP DE ;
E24A 0C INC C ; Down a dot row
E24B 25 DEC H ; Finished?
E24C 20A9 JR NZ,SP8 ;
E24E C9 RET ;
E24F 0000 BRKSTK: DEFW 0 ; Break stack
; ****************************
; * This buffer holds the 64 *
; * colour codes produced by *
; * a cell scan: *
; * *
; * CCCCCCCC Bytes 00-07 *
; * CCCCCCCC Bytes 08-15 *
; * CCCCCCCC Bytes 16-23 *
; * CCCCCCCC Bytes 24-31 *
; * CCCCCCCC Bytes 32-39 *
; * CCCCCCCC Bytes 40-47 *
; * CCCCCCCC Bytes 48-55 *
; * CCCCCCCC Bytes 56-64 *
; * *
; ****************************
E251 CBUFF: DEFS 64 ; Cell buffer
END
```
<a name="character_editor"></a>
## Character Editor
This program allows the MSX character patterns to be modified. When the program is first entered it copies the 2KB character set from its present location (usually the MSX ROM) to the CHRTAB buffer (E2A3H to EAA2H) and sets up the screen as shown below:
The program has two levels of operation, command and edit, with the RETURN key being used to toggle between them. In command mode the four arrow keys are used to select the character for editing. This is marked by a large cursor an is also displayed in magnified form on the right hand side of the screen. The "Q" key will quit the program and return to BASIC. The "A" key is used to adopt the character set, that is, to make it the system character set. When the character set is adopted it is copied to the highest part of memory (EB80H to F37FH) and its Slot ID and address placed in [CGPNT](#cgpnt).
In edit mode the four arrow keys are used to select the dot for editing, this is marked by a small cursor. The SPACE key will erase the current dot and the "." key set it. As the patter is modified the character menu on the left hand side of the screen is updated.
The character set in the CHRTAB may be saved on the cassette using a "BSAVE" statement and later re-loaded with a "BLOAD" statement. The ADOPT subroutine should be saved with the patterns and executed upon re-loading so that the system adopts the new character set. Alternatively the character set alone can be saved and its Slot ID and address placed in [CGPNT](#cgpnt) upon re-loading using BASIC statements. Note that altering the character patterns does not affect the operation of the MSX system un the slightest.
```
ORG 0E000H