├── sprmon64.d64 ├── supermon64.prg ├── Makefile ├── relocate.asm ├── README.md ├── sprmon64.txt └── supermon64.asm /sprmon64.d64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblang/supermon64/HEAD/sprmon64.d64 -------------------------------------------------------------------------------- /supermon64.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblang/supermon64/HEAD/supermon64.prg -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | supermon64.prg: relocate.prg supermon64-8000.prg supermon64-C000.prg 2 | ./build.py $^ $@ 3 | 4 | supermon64-8000.prg: supermon64.asm 5 | 64tass -i $< -o $@ -DORG='$$8000' 6 | 7 | supermon64-C000.prg: supermon64.asm 8 | 64tass -i $< -o $@ -DORG='$$C000' 9 | 10 | relocate.prg: relocate.asm 11 | 64tass -i $< -o $@ 12 | -------------------------------------------------------------------------------- /relocate.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Relocatable code stub for Supermon 64 by Jim Butterfield 3 | ; This code was disassembled from the original Supermon+64 V1.2 binary. 4 | 5 | ; The relocation stub starts at the Start of Variables pointer (VARTAB) and 6 | ; works backwards through the Supermon64 machine code, copying it to the top 7 | ; of basic memory pointer (MEMSIZ), and decrementing the pointer as it goes. 8 | ; Relative addresses that need to be adjusted are marked with a $36 byte 9 | ; immediately following them. Since we're working backwards, the marker is 10 | ; encountered first, then the high byte, then the low byte of the address 11 | ; needing to be adjusted. The relative addresses are calculated such that 12 | ; adding the top of memory to them will yield the absolute address of the 13 | ; jump target in the relocated code. 14 | 15 | ; build.py will build a relocatable Supermon64 binary from this stub plus 16 | ; standard Supermon64 binaries assembled to two different pages. 17 | 18 | ; ---------------------------------------------------------------------------- 19 | ; variables 20 | 21 | SOURCE = $22 ; first temp variable 22 | TOPMEM = $24 ; highest address available to BASIC 23 | LASTBYT = $26 ; previous byte encountered 24 | VARTAB = $2D ; pointer to start of BASIC variable storage area 25 | FRETOP = $33 ; pointer to bottom of string text storage area 26 | TARGET = $37 ; end of basic memory/start of machine code (aka MEMSIZ) 27 | 28 | 29 | ; ---------------------------------------------------------------------------- 30 | ; basic header 31 | 32 | * = $0801 33 | 34 | ; 100 PRINT "{DOWN}SUPERMON+64 JIM BUTTERFIELD" 35 | ; 110 SYS(PEEK(43)+256*PEEK(44)+71) 36 | 37 | .BYTE $29,$08,$64,$00,$99,$20,$22,$11 38 | .TEXT "SUPERMON+64 JIM BUTTERFIELD" 39 | .BYTE $22,$00,$43,$08,$6E,$00,$9E,$28 40 | .BYTE $C2,$28,$34,$33,$29,$AA,$32,$35 41 | .BYTE $36,$AC,$C2,$28,$34,$34,$29,$AA 42 | .BYTE $37,$31,$29,$00,$00,$00,$00,$00 43 | .BYTE $00 44 | 45 | ; ---------------------------------------------------------------------------- 46 | ; relocator stub 47 | 48 | LDA VARTAB ; start copying from the start of basic variables 49 | STA SOURCE 50 | LDA VARTAB+1 51 | STA SOURCE+1 52 | LDA TARGET ; start copying to the end of BASIC memory 53 | STA TOPMEM 54 | LDA TARGET+1 55 | STA TOPMEM+1 56 | LOOP LDY #$00 ; no offset from pointers 57 | LDA SOURCE ; decrement two-byte source address 58 | BNE NB1 59 | DEC SOURCE+1 60 | NB1 DEC SOURCE 61 | LDA (SOURCE),Y ; get byte currently pointed to by SOURCE 62 | CMP #$36 ; check for address marker ($36) 63 | BNE NOADJ ; skip address adjustment unless found 64 | LDA SOURCE ; decrement two-byte source address 65 | BNE NB2 66 | DEC SOURCE+1 67 | NB2 DEC SOURCE 68 | LDA (SOURCE),Y ; get byte currently pointed to by SOURCE 69 | CMP #$36 ; check for second consecutive marker ($36) 70 | BEQ DONE ; if found, we're done with relocation 71 | STA LASTBYT ; if not, save byte for later 72 | LDA SOURCE ; decrement two-byte source address 73 | BNE NB3 74 | DEC SOURCE+1 75 | NB3 DEC SOURCE 76 | LDA (SOURCE),Y ; current byte is low byte of relative address 77 | CLC 78 | ADC TOPMEM ; calc absolute low byte by adding top of memory 79 | TAX ; save absolute low byte in X 80 | LDA LASTBYT ; previous byte is high byte of relative address 81 | ADC TOPMEM+1 ; calc absolute high byte by adding top of memory 82 | PHA ; save absolute high byte on stack 83 | LDA TARGET ; decrement two-byte target address 84 | BNE NB4 85 | DEC TARGET+1 86 | NB4 DEC TARGET 87 | PLA ; retrieve absolute high byte from stack 88 | STA (TARGET),Y ; save it to the target address 89 | TXA ; retrieve absolute low byte from stack 90 | NOADJ PHA ; save current byte on stack 91 | LDA TARGET ; decrement two-byte target address 92 | BNE NB5 93 | DEC TARGET+1 94 | NB5 DEC TARGET 95 | PLA ; retrieve current byte from stack 96 | STA (TARGET),Y ; save it in the target address 97 | CLC ; clear carry for unconditional loop 98 | BCC LOOP ; rinse, repeat 99 | DONE LDA TARGET ; fix pointer to string storage 100 | STA FRETOP 101 | LDA TARGET+1 102 | STA FRETOP+1 103 | JMP (TARGET) ; jump to the beginning of the relocated code 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Supermon+64 V1.2 2 | By Jim Butterfield et. al. 3 | 4 | Supermon64 is a machine-language monitor for the Commodore 64. In modern parlance, it would be 5 | called a debugger, providing functions including inspecting and altering registers and memory locations; 6 | searching, comparing, and transferring blocks of memory; and assembling and disassembling machine code. 7 | 8 | Here is a 10-minute video I made demonstrating many of its features: https://www.youtube.com/watch?v=MEjnMt_3wkU 9 | 10 | ## Contents 11 | 12 | Original artifacts: 13 | - [sprmon64.txt](sprmon64.txt) is a posting to comp.binaries.cbm 14 | containing a uuencoded self-dissolving archive with the Supermon+64 V1.2 15 | sources, instructions and binaries. 16 | - [sprmon64.d64](sprmon64.d64) is a D64 image created from the 17 | archive for use with VICE, disk drive emulators, or to create a real floppy disk. 18 | - [supermon64.prg](supermon64.prg) is the original Supermon+64 binary taken from 19 | the archive. The sources below will build an identical binary. 20 | 21 | Modernized sources: 22 | - [supermon64.asm](supermon64.asm) is the Supermon+64 V1.2 source code, converted to 64tass format 23 | and heavily commented by me. 24 | - [relocate.asm](relocate.asm) is the disassembled machine-code stub that loads Supermon+64 into 25 | the top of BASIC memory. 26 | - [build.py](build.py) is a python script I wrote that transforms the assembled relocator 27 | stub and the fixed-location Supermon binaries into a relocatable binary. 28 | - [Makefile](Makefile) is a GNU makefile that will build the final supermon64.prg binary 29 | using the above sources. 30 | 31 | ## Background 32 | 33 | Supermon is closely associated with [Jim Butterfield](https://en.wikipedia.org/wiki/Jim_Butterfield) 34 | but it had many contributors over the years. The original version of Supermon for the Commodore PET 35 | contained the following credits: 36 | 37 | - Dissassembler by Wozniak/Baum 38 | - Single step by Jim Russo 39 | - Most other stuff (,HAFT) by Bill Seiler 40 | - Tidied & Wrapped by Jim Butterfield 41 | 42 | The earliest documented appearance of Supermon that I could find was in the 43 | [January 1980 issue of The Transactor](https://archive.org/details/transactor-magazines-v2-i08). 44 | From its origins on the PET, Supermon made its way to the VIC and the Commodore 64. It apparently 45 | shares some DNA with the monitor and mini assembler on the Apple II as well as Micromon 46 | and MADS monitors on various Commodore computers. 47 | 48 | The first version for the Commodore 64 appeared as a type-in program in the 49 | [January 1983 issue of Compute Magazine](https://archive.org/details/1983-01-compute-magazine). 50 | An improved version followed in 1985, updated to include the features in the built-in monitors 51 | for the Commodore Plus/4 and 128. This is the version that is preserved here. 52 | 53 | Supermon 64 was widely distributed by Commodore User's Groups and included on the 54 | [demo diskettes](http://www.zimmers.net/anonftp/pub/cbm/demodisks/c64/starter-kit.d64.gz) 55 | and tapes that Commodore provided with their hardware. It was also included as a type-in 56 | program in the back of many books on machine language programming, including Rae West's 57 | [Programming the Commodore 64: The Definitive Guide](https://archive.org/download/Compute_s_Programming_the_Commodore_64_The_Definitive_Guide/Compute_s_Programming_the_Commodore_64_The_Definitive_Guide.pdf) 58 | and Jim Butterfield's own 59 | [Machine Language for the Commodore 64](https://archive.org/details/Machine_Language_for_the_Commodore_Revised_and_Expanded_Edition). 60 | 61 | More than 30 years later, I decided to learn 6502 assembly. After working my way through 62 | Jim Butterfield's excellent book using Supermon64, I wanted to find some real assembly code to 63 | study, and the software I had just been using seemed like a natural place to start. I started 64 | looking for the sources online but for a piece of public domain software, it wasn't as easy as 65 | to find as you'd think. 66 | 67 | I found [this thread](http://comp.sys.cbm.narkive.com/KUAL6oqM/attn-jim-butterfiled-i-m-looking-for-supermon-64-source-code) 68 | on comp.sys.cbm where someone asked for the sources and Jim Butterfield himself responded, but 69 | at the time he didn't have easy access to the sources. One person pointed to this 70 | [modified version](http://www.ffd2.com/fridge/programs/supermon.s) 71 | but I wanted the original. Someone else mentioned a file called SPRMON64.SDA at a now-defunct FTP 72 | site. I googled for the filename and found what was apparently the last remaining copy on the internet 73 | on a [gopher proxy](https://gopherproxy.meulie.net/sdf.org/1/users/rogertwo/prgs/cbm/c64/programming/). 74 | 75 | I downloaded it and found that it contains what appear to be the original source and binaries 76 | for the updated 1.2 version. Since it doesn't seem to have an official home and it's 77 | continued availability on the internet seemed precarious, I decided to give it one on GitHub. 78 | 79 | I modernized the source code so it can be built using the [64tass](https://sourceforge.net/projects/tass64/) 80 | cross-assembler. The original source (included on the D64 image) was almost completely uncommented and used a 81 | few constructs that were unsupported by 64tass. I converted these to the equivalent 64tass constructs, 82 | indented the code, and then worked my way through the code line-by-line until I understood it all, commenting 83 | it as I went along. 84 | 85 | As Jim noted in his usenet posting, the provided sources don't produce the final Supermon 64 binary: 86 | 87 | >I should note that, since Supermon+64 is relocatable code, the source 88 | does not assemble into the final binary file. It may seem crude, but 89 | I follow this procedure: (1) The source is carefully structured so 90 | that there are no "dispersed addresses" such as might be created with 91 | something like LDA #>VECTOR .. LDY #0200 4d 20 32 30 30 20 32 30: m 200 20 183 | >0208 39 00 00 04 00 04 00 04: 9....... 184 | ``` 185 | 186 | Display memory from 0200 hex to 0209 hex. Display is in lines of 187 | 8, so addresses $200 to $20f are shown. If only one address is 188 | used then 12 lines (96 locations) will be shown. If no address is 189 | given display will go from the last address. Equivalent ASCII 190 | characters are shown in reverse at the right. Values are changed 191 | by typing over the display followed by a return character. 192 | 193 | 194 | ### Exit to BASIC 195 | 196 | ``` 197 | x 198 | ``` 199 | 200 | Return to BASIC READY mode. When you wish to return to SUPERMON+, 201 | command "SYS 8". 202 | 203 | ### Simple Assembler 204 | 205 | ``` 206 | a 2000 lda #+18 207 | ``` 208 | 209 | changes to: 210 | 211 | ``` 212 | a 2000 a9 12 lda #$12 213 | a 2002 ..next instruction 214 | ``` 215 | 216 | In the above example the user started assembly at 2000 hex. The 217 | first instruction was load a register with immediate 18 218 | decimal. In following lines the user need not type the "a" and 219 | address. The simple assembler prompts with the next address. To 220 | exit the assembler type a return after the the address prompt. 221 | 222 | Previous lines may be changed by typing over the right hand part. 223 | 224 | ### Disassembler 225 | 226 | ``` 227 | d 2000 2004 228 | 229 | . 2000 a9 12 lda #$12 230 | . 2002 9d 00 80 sta $8000,x 231 | ``` 232 | 233 | Disassembles instructions from 2000 to 2004 hex. If one address 234 | is given, 20 bytes will be disassembled. If no address, 235 | start from the last used address. 236 | 237 | Code may be reassembled by moving the cursor back and typing over 238 | the right hand part. 239 | 240 | 241 | ### Fill Memory 242 | 243 | ``` 244 | f 1000 1100 ff 245 | ``` 246 | 247 | fills the memory from 1000 hex to 1100 hex with the byte ff hex. 248 | 249 | ### Go (run) 250 | 251 | ``` 252 | g 1000 253 | ``` 254 | 255 | Go to address 1000 hex and begin running code. If no address is 256 | given, the address from the register is used. 257 | 258 | ### Jump (subroutine) 259 | 260 | ``` 261 | j 1000 262 | ``` 263 | 264 | Call address 1000 hex and begin running code. Return to the 265 | monitor. 266 | 267 | ### Hunt Memory 268 | 269 | ``` 270 | h c000 d000 'read 271 | ``` 272 | 273 | Hunt thru memory from c000 hex to d000 hex for the ascii string 274 | "read" and print the address where it is found. A maximum of 275 | 32 characters may be used. 276 | 277 | ``` 278 | h c000 d000 20 d2 ff 279 | ``` 280 | 281 | Hunt memory from c000 hex to d000 hex for the sequence of bytes 282 | 20 d2 ff and print the address. A maximum of 32 bytes may be used. 283 | 284 | ### File Handling 285 | 286 | #### Load 287 | 288 | ``` 289 | l 290 | ``` 291 | Load any program from cassette #1. 292 | 293 | ``` 294 | l "ram test" 295 | ``` 296 | 297 | Load from cassette #1 the program named "ram test". 298 | 299 | ``` 300 | l "ram test",08 301 | ``` 302 | 303 | Load from disk (device 8) the program named "ram test". This 304 | command leaves basic pointers unchanged. 305 | 306 | #### Save 307 | 308 | ``` 309 | s "program name",01,0800,0c80 310 | ``` 311 | 312 | Save to cassette #1 memory from 0800 hex up to but not including 313 | 0c80 hex and name it "program name". 314 | 315 | ``` 316 | s "0:program name",08,1200,1f50 317 | ``` 318 | 319 | Save to disk drive #0 memory from 1200 hex up to but not including 320 | 1f50 hex and name it "program name". 321 | 322 | ### Transfer Memory 323 | 324 | ``` 325 | t 1000 1100 5000 326 | ``` 327 | 328 | Transfer memory in the range 1000 hex to 1100 hex and start storing 329 | it at address 5000 hex. 330 | 331 | ### Compare Memory 332 | 333 | ``` 334 | c 1000 1100 5000 335 | ``` 336 | 337 | Compare memory in the range 1000 hex to 1100 hex with memory 338 | starting at address 5000 hex. 339 | 340 | ### Disk Operations 341 | 342 | ``` 343 | @ 344 | ``` 345 | 346 | Get disk status message 347 | 348 | ``` 349 | @9 350 | ``` 351 | 352 | Get disk unit 9 status message 353 | 354 | ``` 355 | @,$0 356 | ``` 357 | 358 | Get drive 0 directory 359 | 360 | ``` 361 | @,s0:temp 362 | ``` 363 | 364 | Scratch file 'temp' from disk 365 | 366 | ### Output to Printer 367 | 368 | Call SUPERMON+ from basic with: 369 | ``` 370 | open 4,4:cmd 4:sys 8 371 | ``` 372 | All commands will go the printer. When complete, return to basic 373 | with "x" and command: 374 | 375 | ``` 376 | print#4:close 4 377 | ``` 378 | 379 | 380 | ### Summary 381 | 382 | - `$ , + , & , %` number conversion 383 | - `g` go (run) 384 | - `j` jump (subroutine) 385 | - `l` load from tape or disk 386 | - `m` memory display 387 | - `r` register display 388 | - `s` save to tape or disk 389 | - `x` exit to basic 390 | - `a` simple assembler 391 | - `d` disassembler 392 | - `f` fill memory 393 | - `h` hunt memory 394 | - `t` transfer memory 395 | - `c` compare memory 396 | - `@` disk status/command 397 | 398 | ### Restarting Supermon 399 | 400 | Supermon will load itself into the top of memory...wherever that happens to 401 | be on your machine. Be sure to note the SYS command which links SUPERMON 402 | to the Commodore. It may be used to reconnect the monitor if it is 403 | accidentally disconnected by use of the run-stop/restore keys. 404 | 405 | ## License 406 | 407 | To the best of my knowledge, this software is in the public domain. I claim no ownership. 408 | 409 | The comments in `supermon64.asm` and `relocate.asm` as well as the entirety of `build.py` are my own. 410 | I hereby place them in the public domain. However, I would greatly appreciate attribution if you make use of them. 411 | -------------------------------------------------------------------------------- /sprmon64.txt: -------------------------------------------------------------------------------- 1 | 2 | From KurtB02@aol.com Mon Aug 28 20:07:18 2000 3 | Date: Sun, 27 Aug 2000 21:33:43 +0200 4 | From: KurtB02@aol.com 5 | Newsgroups: comp.binaries.cbm 6 | Followup-To: comp.sys.cbm 7 | Subject: Supermon+64 - (SPRMON64.SDA) 8 | 9 | 10 | ****************************************************** 11 | *** *** 12 | *** comp.binaries.cbm is a moderated binaries- *** 13 | *** only newsgroup (no discussion or *** 14 | *** crossposting allowed) for Commodore 8-bits *** 15 | *** *** 16 | *** For information on comp.binaries.cbm visit *** 17 | *** http://stockholm.ptloma.edu/comp.binaries.cbm/ *** 18 | *** *** 19 | *** This file is also available via FTP from *** 20 | *** ftp://videocam.net.au/cbm/incoming *** 21 | *** (allow time for submission to be received) *** 22 | *** *** 23 | ****************************************************** 24 | 25 | 26 | This SDA contains the Supermon+64 machine-language monitor, a docs file, 27 | and the source code for it. 28 | 29 | Directory for: sprmon64.sda 30 | ================================================== 31 | FILENAME TYPE BLKS NOW V STOWAGE DATE 32 | ================================================== 33 | sda232.64 p 9 --- - -------- 34 | supermon+64 p 13 12 2 crunched 01jan1980 35 | supermon+ inst p 29 19 2 crunched 01jan1980 36 | supermon+64 docs s 24 14 2 crunched 01jan1980 37 | supermon+64.src s 63 32 2 crunched 01jan1980 38 | ================================================== 39 | Files: 4 129 40 | 41 | 42 | begin 644 SPRMON64.SDA 43 | M`0@-"`\`GB@R,#8S*0```*D`C2#0J0"-(=!,1`H@10@@AQ`.#<1)4D5#5$]2 44 | M62!&3U(@5$A)4R#3+L0NP5)#2$E613H-`*`EJ3T@TO^(T/A@3"L)(!P(HNV@ 45 | M$(8OA#"I#2#2_Z``(!H*R0+0Y,@@&@K)!K#<.*4MY2^E+N4PD-&@"B`:"JK( 46 | MJ2(@TO\@&@H@TO_(RM#VJ2(@TO^I("#2_Z73R1/0]:`)(!H*H@3=?PGP`\K0 47 | M^+U_"2#2_[V$"2#2_[V)"2#2_ZD@(-+_H`@@&@J9DP.(P`/0]:V7`XVF`ZV8 48 | M`XVG`R"."2"'$"!"651%4P"MF@.%(ZD`A2(.F@,NFP,XI2+MF@.-F@.E(^V; 49 | M`XV;`QBE+VV:`XV:`Z4P;9L#C9L#KIH#K)L#3%H(((<0#PH@S/^I""##_ZG^C8(#(&4.K6T# 58 | MS74#T!"M;@/-=@/0"*E/(-+_J4LLJ3\@TO],:@HLE@,P`CA@(,S_J0@@P_^E 59 | M`0D!A0%,=*2B+ZD`G5P#RA#ZJ3"-``&I.HT!`2!E#LD"T,WH(&4.G6P#Z.`) 60 | MT/4LE@,0`R":#"!E#LD1L+*J&&D$A;>IAXV%#JE9C5<-J0Z-A@ZI#8U8#:`` 61 | MJ0"%NZD!A;P@90Z9`@$@[PO(RM#SJ2R9`@$@[PO(K70#F0(!(.\+J2`@[PNI 62 | M"*J@`86XAKJ$N2!E#B!E#B!E#B#Z"ZQL`\`%T`6I_XUQ`\`"\`W`!/`)P`'0 63 | M`R!E#AA@H`"8F0#,F0#-F0#.F0#+R-#QJJD`C7L#C7P#C7T#H`4@.`YJB-#Y 64 | M:FIJC8H#R1FP-,D`\"FH(#@.+GX#+G\#+H`#B-#QJ&Z``VY_`VY^`RY[`RY\ 65 | M`RY]`XC0ZR`1#.C0LLYZ`QA@+)8#,`%@3-+_.&"M;`/)!K#WK70#R5#P",E3 66 | M\`3)5=#H8(XW#*``K8H#S'H#T!^9`,NM>P.9`,RM?`.9`,VM?0.9`,[N>@.B 67 | M`8J9`,]@V0#+D`/(T-2,3PR@_B!;#(C``-#X(%L,K8H#3!X,N0#+F0'+N0#/ 68 | MF0'/N0#,F0',N0#-F0'-N0#.F0'.8%-43])004/+4U%5147:0U)53D/(4U%5 69 | M05/(0U)53D/(((<0#55.+0"@`*YL`\HP"[EZ##`#R-#XR-#RN7H,2"E_(-+_ 70 | MR&@0\R"'$$E.1RXN+@!@CAL-C!T-("(-+(,#,#ZNA`/P!B"3#4P6#:YL`_`= 71 | MX`'P&>`%\`3@`]`*((0.L!VN;`.0"B"J#;`4D`,@90[@`/`'X`+P`R!6#2!! 72 | M#1BB`*``+(,#8*YO`]`6KG`#T`ZN<0/0!JG_C8,#8,YQ`\YP`\YO`V!([H4# 73 | M384#&&UU`XUU`Y`#[G8#:&!,60W)_O`!8*EMC5<-J0V-6`UH:$SF#(V'`ZE] 74 | MC5<-J0V-6`U,:`V,FPV@DXQ7#:`-C%@-K(<#C(0#C8<#SH0#\`+0#*``J5F- 75 | M5PVI#8U8#:V'`V"I`(V(`XWU#8V&`XV+`^Z+`XUW`XUX`XUY`XP>#HX<#B`X 76 | M#I`,K(8#K8L#&7<#F7<##HL#D`8NBP/NA@/NB`.MB`/)&)`$.$P;#J``V0#+ 77 | M\`20S;#QO@#,['<#T!F^`,WL>`/0$;X`SNQY`]`)N0#/&*(`H`!@R/#-C/4- 78 | MS'H#D,OPR4SP#0$"!`@0($"`C&`.C6(.K($#T`8@90Z-B0.MB0,Y,`[0`A@D 79 | M.`C(P`B0`J``C($#H`"I`"A@C((.K((#P/Z0#!B892^%+Y`"YC"@`+$OR(R" 80 | M`Z``8$R'#B#?#R#(#R!8#XU>`XUK`XU@`ZU=`XU?`ZFNC84.J0Z-A@ZM:P,8 81 | M8"!8#SB-80/M90.M70.-8@/M9@.0)ZU@`XUK`R"O#ZU>`XU<`XUI`ZU?`XU= 82 | M`XUJ`ZUE`XUA`ZUF`XUB`ZU=`_`PK5P#A12M70,)\(45(%$0(*\/I14I#PG0 83 | MA14@41"-7`.E%2D/">"%%2!1$(U=`]#+J32-A0ZI#XV&#JU<`XUK`XU@`QA@ 84 | M(+H/L`%@K5X#C6D#K5\#C6H#(/P/K6$#C5X#K6(#C5\#3*X.J0"-7`.L:`,@ 85 | M.`XN7`,N70.(T/2M7`/0.*U=`\D!T#%H:*D`C6\#C7`#C7$#J?^-@P.M;`/) 86 | M!=`6H!`@.`XN;0,N;@.(T/2@*"`X#HC0^CA@K5P#3%X0H`"1_>;]T`+F_F"E 87 | M_(0!D12& 90 | M`5A@I?PI#PGPA16E^X44H`"F`7B$`;$4A@%88$BM:`/)#+`?K6,#T`/.9`/. 91 | M8P.M8P,-9`/0#.YH`PYG`ZUG`XUD`VA@:(V3$&B-E!`@HQ"M___P!B#2_TR/ 92 | M$""C$&R3$.Z3$-`#[I008``````````````````````````````````````` 93 | M`````````````````````````````````````````````````@-@_&,,``P` 94 | M4`M355!%4DU/3BLV-/XA``!!H`3!!(!,!(@@4J9*@2@ELN3)H<:&Q1\0!*1( 95 | MDD5`B"H5*N&)D21*A@@B`(1!L`,@#X4A%%@PHU2%"2LV-1FP8,F"'2-*?\`` 96 | M*$4K%)%2=$(1*\4N%(E2'$*1*BB,R`(@8XB-(=*(R"0V"^B55JM1JTX];!XP 97 | M"$76N'K-VG5A,$6B2BDRIDA)E&F/PQAV%A*QB^J-O6__@=AA2-!41BCF"D*Q 98 | ML,$.`!LV!0@0R/4'T!\VM7/]^<NN/8H`B,,("`'S_),+`$!<<``%#>`P 101 | M"2P+/-I(+O\`0@"[-SRPSR0$//!.$6CL,F`)#T"[S`<%X)GM/C)X\0H;!R#3 102 | M`!('P-,`"E%`444OZY8@02]K<2(#ACW$P@00,F3Q#ALR['B##4`4T``$!"AZ 103 | M`T'\L)$`&)5`6\8"L-$`<@H%`9"R/30` 105 | M63B)#/L,!`D\44FB1_SGD%,)U+E)5@'>X.$%0$A0"V4-U!95(]0`40<-0-1` 106 | M"Q`"50)4-2(Y$5,?<7436P<06!!;%#)HY&^W#?!5DK4]JO!@&W?VT,BP5]Z4 107 | M1P+/%$`6F7EF`-`>55#2SR1$/`!!%)3XATX4#0!1"5TE'7&*@8W0.)%@GP`0 108 | M"WB(M.8!:.FI`IQO5-`D!`+DE#+,*(.4,L(I0S2`2RWT`&'*H&74PC`A0U93 109 | M!""]0`-(*L,1#32X'UUFG2V,[8T($`@`D<@@BPRVU3ILT`P.`]"<,<@"")PQ 110 | MQ`(PP\,&0>XKD>4-,C`F!@%Q*>&3!U,E0#!DB6BZ$S)L3$+.`F@A><,D`#P@ 111 | M22;H`#*QK99@,``>"SSS`!($H-I3^,Z[#D,#('L%0TQ"LZC2'IN[L``D$\PD 112 | MM,_"L_6X]KIY<%C"BA0`2`A=A-!)!"&T$T("E0`22@^5H!-*]^X\H!EZF-UO 113 | M39V_]`:\(Q?=\P`,B71S1KO/X)'`[8K^^+>Y5H=!73BA=!T&42DK%0+O+)0Q 114 | M2`E#U";$),HLL&@CH712V3X;_)?P(4!,HL8#8$PBPP)=-D+`*O^4D@`E$/"` 115 | M<]_C`54H`(7.F:\$E&$8)1CQ.3KQ@&4;R0.:#H.*W;$`>:HH6_NR<99UK&0] 116 | M)>)>"6@`NS?XA3C``1!Q@!"BS;$`$!S8`>=NQS5R`01`@0(8"[C&%!B`CMYP 117 | M@@.7X$BP`%H00(`]>``I&G"/2I`@ 118 | M-[*X0`%`4`LH`&)2^0#%3.C\C%++24:?8(A)N"*/3\SQURV```4+6.`NT7C`!61P 125 | M@0^PX2YQJ"L7#"3G#<-(#95`D9:AI2=5X(,`(=QW)E!``,+"%KZ(7QA(O)XZ@0Z4U@:$Z`S6:4Q&1LC#CB3@0J4.\^1<=)0(=!&#A#18`-8RHMP.(LG,[H#$)&CP`O$)6 132 | M!`=(@#%B_0.`9:Q&;.@8@@Q(N`5X;A<6@+$K`%H"7$2X&`9Z\,(2].*#_;%R 133 | M"XPC`@8D0`^3"088+@&.22"@`2J(!20`,3-])$`*#Y#`7>@P`*@1F`JM&$2N 134 | M:46$NE@!#(,@,P[H9('W66(!&(`S+>@``T`T`!^UIC61218(4U,8!_!CG/[" 135 | M,8BH1;$:"<@:/3`0$RM8H`(6J!J34=L'"42U&!8MYBD6`"\H+``:E8`%"9"! 136 | M8&@>HQ)0D.%Q07CF6\1ROYQ"++5*$%4Y`*X-_^,&)6]0!8MN=W>J2``0+`$& 137 | M2S#`8!CK=3>`W`L@/V,27'CP`_@!@3O^;@@4,/`RAN.$4(S``!P*SS[+XI1C 138 | M5NL?@(@MUVXQB-MR[0%#N!P+AG:3.5SNU039W!]B3;87.F,8H1T%$,YVO5L, 139 | M'_BPQMS?KJJ@,.]A"+%J1@P7JP#B*BD$V`I'K&GCA-@%2IEE:1XL:C&GVOLU,?A(76.#[Y\RB\'%_"95M^BI; 146 | M$6,"$'Q]#TC``HCX-_QC-?_"`G##'!/``EQB+=:[IDM@>\!89P"J`)3Y'^!0 147 | M"4;#([-P)NG`"=DR=.#"`WR[(BQ``Q#X@97ZJ=?XD,_Z(2UA#\A8MT)A#ET( 148 | M&)ZZAAK@L#`<#"'[:!`@AC%BB#$A"`8,B` 149 | M1LB#9LB#,BB$)!"`&C`"'3!!+.!"/]`!'2B#)BA#`7!!$A`@/"B!/"R!(&8" 150 | M`12```"!J/``!`AX'A"!*###0K@#9#B'=_B&1P1`3$)E"" 154 | M,H0`,K``8FP",13`!AB!%D@`&1A!"D2`"HC`!*B`!8C`!:0``+0`!(2`"[0` 155 | M!=B!"[S`#$B!$SQ!"40!`%1!$12%)+B`$XB`!30!$S1%!```%F`!&9"!$SA! 156 | M!$S`"$P!&4B`"(A`$4R$!+:%(HB`&(B#!$B!+"B!"FB``-`!'W`#"5`#&9"" 157 | M#"B#&NBH%+20$UQ*'K"K%/"3$W"0&.B2$RP1%:@3%;23$\P3%21@?8H!&^"3 158 | M$T"0$R``%$``$(``,``#($@2%6`2%0(````````````````````````````` 159 | M```````````````````````````````````````````````````````````` 160 | M```````````````````````````````````````````````````````````` 161 | M`````````````````````````````````@-GOS`<`!,`4`Y355!%4DU/3BL@ 162 | M24Y35/XA``"!0`<""H!X!)2I4B!*B2QY''`$"*B2((J!$E"#E_,3(4ZI0*5L.`#*`8"J>FU+>_)DJD$=`@1)Y:B2) 165 | M5.Y$E!P5DH0)D@%@``CVXH3)DR5+D!R1S%3:5"1/N7+!NI4)425)A@K:*`"< 166 | M`,%3O`S)474F5*P\:`AY(K4K8\2$`QT`T$`)%H=8?@F"\-%R92))B,Q2$L.( 167 | M$\U,K#U9`)B!%I*)J!HE,B3#YLJ3+VH!8B%#E\Y/L"HN`/2`DBY>'BW:SH1) 168 | M#B53H`ZA*/=H1B9'@2J5+&3(D*=.E`C<`!`&*`J>0N`Y(C(A+20J"**"JLR& 169 | M(0*!988:+C,4"$`9`!6`HAJHYWX;ZS(4JLDK+)1"HJD[@FA83S.@F(@+`9S\ 170 | M`2PQBX!B9PN((,F&*K((N6P002@"LQ)`#1I44`*``&"V* 172 | M\P>H+R;*@I`.*B.*!H+(HF!_@<$+"P`^@-&GN(,AH(RT?Q"HI8[MR,4OK)X0 173 | M((JB*:CIK1LJ*`-`!6!,*&X4!"H5[J77>D.04FS0V::>JD7YC#(2_E3)"F`L 174 | M5:=>I`$^ 175 | M(.=3($(HJ`=\MHA&:S*VB&*&)+1 177 | M`D`PX-``2&PLL0%``>"P`>@LB+9ZC8+:P>@N&_+#!`9M$DP#CL!\4LOBAV$^ 178 | MZX%LTP6JY9<_HDP\#!JH0`,`;X`CM0*:R*-J!X(^5^[R*OTGJ$Y[.XQF7#9` 179 | MH+)"B$,FXJK\J)OJ[1!6REZFVA!L@:KU,`"!W@J%%5$4HAX@!`06*R0'$K(0 180 | M0`T+#'#"!@!60."T`@2QU@G!@7Z%C&"Q_:TN/$H^!X%R4=L`0#(0^*PPW#FM 181 | MU(H@(HBHBK+,YU]X57N6`$`_$(2M<-L=:+9D>[,//^I(Z)!($%L4U^C#-A`D 182 | MK#!$_?X@=Y6\R)KJ'C*OI@T$66<"G+RL4,,$`RQP0-`G!%$`8CGB#^`D!AD@ 183 | M4>)R5[K:049EJ:$(:Q@AJ9[JC),`^WD`04"BSJ]^!P%(_`T&,/@/(F0%,4C` 184 | M06)8`1(=P84D-QT%`H>"G!_&)[?^3&MZ:'B-2@!`(B`Q9`7FLS`PK@VJ\M3@ 185 | M1`]"AO7(!B1>S&8.$J,#D/@#8-3,+QZJ0-3X1..RER%C&TE@%)"``)#YG44, 186 | M'`$22#$GM>2@`&'*0YDZ$(P3-*@#3>JX'0!1PQ\@@1,[<+N"1"```Q@3!J:$ 187 | M@3EA8$D86`$`6D"!+'9`E%^8R.051JH[.F0R#'4TH2DR0QF"Y(8(`*`)*##$ 188 | M#NI4DCB0*$5Z(B+)U517(=T)RS]7I=`8"`04N&+'4#X;`L(<-)$R(&15:$+! 189 | MNA2@&1,!@'I`021V,8B1M?C-7R%E4G]4%1R4))Y1[L4M^:S'+*`@EU3*QU%@ 190 | MM*4]Q*&D"$-"-1W$P!6@H!&[.%/'TD03K."C'=WD`P!H@(91OL@"82W%0TOQ 191 | MRBT$"ZTE^SBR@,DC#P#`"&C8%,+MNP#&G>EDW_6 194 | M^.PF:O/9_8FL@68-!$C`X"-VY7-!)$5+^3H0F,1CC".S%&N@^F9PDWM4]@74?<5#P`"F"2"B?.@IX#!O-K65?F@(",)& 196 | M-R9"#@"(!@S06LH:_Q&%(YISH?\@3R]Z$`'*/@BLSS70`M:C`S`@:&DK($>; 197 | M5GN[]6V4>(-]V`;(`P"K@`%52^O*]ABRJ\L>9SV```N6EJI0Q290F<6HSBM( 198 | M"IF#>=0!>H?WQ0EKOHT4@;4-@(VX-#64J'8)PW0 202 | M)#,XD,1ZD`(.%V..$@`@!#@\32Q:OG$R63P.T:!SN@`,80#A+>5`,E<-DIC6X0%EE9`$.""L`$5G5'F((5IN%<<0`'.!!M[K\02E(BP@:^T;&CS4VP%6>!1?@P;$ZT].<'T39Q*8Z 206 | M+'&HIXW?'LYN*`"`*```5:K8_M9%JWLD^X_^Q*'(,22GP70 209 | M`0$NE]OJ1S=!!!#0`)"QD!@E0,#(%IT%D:6(K:PA`@N2`(!A(&!N 211 | M=>K<]9J)+ZNU@R#DK-DL$+#QW"HD"IQ"JD$H!4W64$@9-),"`A!Q](3:D^D* 212 | M9VLH48F^#ZYQE.,``#<0<;.Z7:N^S(E3HZ>Y282!WJ`A`(`Y$/&VNJYIJ<*9 213 | M-`R9N^F0]RQRT1!A/1*`"`"`71`D0`?5K$"GVM-[J 214 | M&0#`'"`"BG^A/`:A,UO+,"J;NI\XFZ%C!SJP+@#`!R`"GG]A.P@;)6K@O;@1 215 | M%/LXB6$RL?O*`L`#$E#XET$8%'[(&"%YB)U@/6`"$G@`J$<#`#`!$@3YEW$1 216 | M+]-SK3IQ"TF`,B!!C'^Q`&>!F#+RGK*0/#`"HQ`I(XBA$`0"$J2%B*0'#\A( 217 | M-BH'G[K@..*3L@:@-0!`&2!!@7^%C+I3F/.#!`7Z'BJ'@MT``#0`$NP$L_G# 218 | M*"#!67`"L&B!.-H`A4V"G%4"`&P"$CR$JDF`>$B0%H`!,GR%*OF'K_'I*3*' 219 | M9!BP#PDROK8`%B`#"(#A;`2`G,P&&(AB/8@!LN59(2+CIBYJLO]I*#+[-I.X 220 | M`0"`#2`#1N0!D3^C,O"((_`IJ@-JO?]@/40`,A!%AVB!%/&#`LTA%`BA/;YK 221 | M3P1!#B`#38QS%DA%%'/"!"`#6ZR1/WJ)F*]$*F_0>61@)KP`,@A$1Q1$$59$ 222 | M"*Y?M1Q-2KIE_&B 225 | M%6K)BY^A/#;#(Z#`5$)A*&C@-?'CAHI(E!SIF^2[O1@Q1`P!`&`#*JPD-F'#L.G95*@3=H``+@! 227 | M*EPD5#)P!+9C'!6C&PAC/&Z&+3*K<#I0)@I!":#"4T)Y<+(Y<0IK*E[P6W8G2?HY2ZZ@_Y1!`#`,Z"! 233 | M7X%0!&441%X55D$#+J!#1$ZB(EJ9,YUCOY1JKZF(DQ+)*U4"':!#4@[*BY5( 234 | M%P``!Z!#5S:;/T@".L0`5`*$-_%*"7*K,H0".J2580Y)@S(62MX7>K%P3H&" 235 | M:-D7&A`5/C`6OE`"0^&#L%"J,C0".A24=>*4J9.A32YA7'YYB40D*/<#`.P" 236 | M.E25=1&$!6@``4(;'R;CAQ/A"PC3)$$8%C"4/Q(#,&!`2UG'"2&5"R+C!R"5 237 | M$[(5=T&#(&!`7X5R%BB77>&4`Q"I;#662[D57A$`#=8(48V"&+`"@RJ#'6#` 238 | M1'4`"$*6E/$5E3`9A+`#L1!7O(]"%F#`4EG7F!#3;&69,KZSN+V"7!6`:A4Q 239 | M,3#6)!+#`&#`3E6%)?M4K?%6@]SG936Q>D'#%&#`&4`%"UP`!CR4-1**%J)C 240 | M&"B#&"XGJ"H#%&#"5W68!*C$9EJ%>5H!*VD!!V`""$"+!?DW%*(V+.HDJ*T$ 241 | M%`YA-6F!&6`"7!,`D7J@36-&&IW[(T&CMLP:+\&Z&V`"1C-$,A`T=M)36`P# 242 | M)D@!=+!`*6`J=0(`B&AA6="&5A(%ORK#.&""7C/D%""!<*.'1,@V%FG!"V"" 243 | M16M)>BMJ;GHG<5.@2E.%'G,"/F"!4S,T$^*W01@X>%@GJ#\&(G`!%OBT@`NG 244 | M2`O748L"`(@`%M2UI/F#-F!!1VNU`BHI@610&DF`)6#!4)N9!`AX!L0"%LRT 245 | M622`*'[4JOP1G-OS-@CB,`!`#F#!6F>1/`@`IV?`.&#!01=J0Q=W"D')B_&J 246 | M;I%>!*`#`!F#!53<`$@@`C1!X!GP!%KQT!PYW2:VCLA`3!*K2@3H# 247 | M`"``MOCU5,^`LLHR#$5Z!G`!-D``3!,B)`C+(ZC(*&5B*BH+BT6!`LH5)4%V 248 | M.&`#$L`$"Q@"-N#T-5*+%EN""KXY,=JDH.2K,D0`-I`-,"#&`=Z:-3+D.^>4 249 | M3%CA`50"-C`,(LH#$E-H$[``#;``%PB#+"$-&2B;6S%`DBK#$F`#UR"B-]Z6 250 | M.$:(*-Y&8C%G#L)B/7P"-H@,,,J;D1QB,"`"W7RE>7JR6&@# 257 | M`(P`+LC,5Z.A@CPV2!!`<`'"S`"'E0!_#A(`VY+D[3H9KYA`<:!#&XJ,48++^@!'K0L/1!A5$QC 259 | M0H+:<(I*I\RCHH'A().A&U6"-^!!WPJ:,L:3.$"# 263 | M`.!#I(H@,50#/E``,)GY`248*41R:F%"_!@$%@AP[/$K<1%?((*%`B3XTDD83KB9*C0I(P 274 | M03)@"9,G2Y8@.2)-5LPC3SGAZ'D)"5$E288*VI1@R68EN6+NB:D(=N\E3X3\ 275 | M=`08,>%`UPFLE?F7*1==EYD0F82$FZK*PPULL,%+AS0$7BLK 277 | M8[@.N&6/?0YH`@4"4$84B62.&@8!%&;(;!B44:!I"X)H>*HS$#"L 278 | M-VMZL,RDWA"(!0\D,.D$`WS`J@M!&FFEJQ[4<,`()TP900!9FH#`D"6D,/(' 279 | M;';I`@J!.$Z&*X^A?J*!(+(H^)"GC/[QEYOFFI@IIPKFY(D%9*D%\((=D!09H`PZ!V81!X81JB!:J,,A*2.V1^&N#$HJ0P$IE&J74A4$A9AK 282 | M-ZK(J%"41.Z':T`)"!6:F\-=1H>L8?4+DSRP+S++'"('.LB,0\*T('EZI.$#"H 284 | MJ/TJVUIBHWE*+*GD4DJM'!JH0),NP%IJ#8%]*I<5UQ2=(]NEN8I/,;N*?4>C 285 | M@MW=!V"IILZ&TJU[!BY>K%:HMLEH?@7X$#2P\/@!-2PPP`E;.E6POJ);"!;2 286 | M68DNMR*-=`4BJ#)BOI?20$4((H*(Z3)!L,EFT.4NKJE[[8+,;K*[65ZX^QT0 287 | M$R0Y5MIL%5)(JJ*$U4N.W*$LA^2L?,B@.=L&4,,$`RQP0-`A@A#GP!_@:9>R 288 | MU5G=+M.#\N6$*Q.B"ZV7)43Z0W(U##!6RA!4"M.A 293 | M`E4H$9=/Y*,:Y:"D33>S*N"@915K($Y5?F@__%"2+L!%P=S8B3*&-&%ZJ&(1 294 | M=^%"F.R!)7O@01`["":4[)JGI72!`H+XDRX6H8]U$!,I"DU2G560)V4L>&9# 295 | M%7LBQ>`,"/C`X"I/@H4*K@(!F(,#9D^F*<6I3R,8P&7N%'O258!-A:M<"V9P 296 | M!L.3*7%U1H*?"5>G$YQNCI7<0AKU2-B!^W;70[-H5V'1 297 | MA.+\8:"`&:,*<)"=64ZP/R4Y1A-II(3#>?:J(,.@!J@[7^Y>):M+&<7`W#F) 298 | MQDI1"!682EP>N8Z4?`6%S5RG<A"`&-1@!$=`5`&*F/*%)LH!5]%T 304 | MU@[A9IA'0?.49YPBB\JF.S@@`!WH&&@6C?(:2E#5!$M#+`]N0E#>)(2H=11( 305 | M0FYJ-PR:A)(>W<@L->?E`A9(I#W%J8P4`YXZLC5*%=%K@'X+VN8L\>*"A[,)!07,]-/GHR.10KD%=*S[EJ8/=[ 308 | M,"JT(V%M&EP[S4$L%O+L\81H8C`%5"7$6*@4640]/)J8)Y(=K@X5:Z:*49B1 309 | M42R69Q6J1%$^4P$*Y)N.#5(KK72#!7Q258I#.P7!SLBF\^WNXOR[`@)P4B$E 310 | MGD^61\$A+=!`(\19]-78^)`3`4&EN]TG*TU*"UFE=(K#J0I!WXM+ULE/0RDT 311 | M*BG9TDHERM&D&+IT9!@G/7:(%,>K(RTSFV[GQK73<+"-%"[Y^:H48PXV/ 312 | M.)5UR_2E3Z965.:R`2&N%&Z2@>$ZU5=R$C?K&K)X6\R?0Y5!,&=6SR"K&^''/G;9<^4H95_9/-1X 314 | MY;DF$N[WD!(E%L:)"`HL815SFK4!J?>Q?XRSDL,E*,@A')\X7>J_*:KH+N1\ 315 | M:B!VLV$]Q=M4056Q>A^NNH0KT#,WP1.P_2T?P\([^RD.=:I!PP7WO/Z_4020X^WWHPQ]]T!5R:U[98N539*>*I^F4 320 | MFQQ3&ILAS'&O'BEIAY5`$,X\:13$FU(HQBH>'Y88@#Y@ZA6A5F@2T=DA'78`0T>\404@QBPE^!P>VG>,QCA1ZKUDX7X,D9M)HH63R0,0Q)ZGZCEMDARCP2"_ 330 | MVE.LVROP"'ALQWW\Q#Y5V"P%J&:<,HW*QZ5K46G<.!0.QXXG.D*FTU\ 331 | MFD;"Q)^C`2S_P#),(P(FP*`KHSYI4S4 333 | M,P`2\B328CI-R&IR`@TH2P?*44!J(P@(HRJHX),#4!!O\@`H8+)/!Q^>N2*L 334 | MP((K424BP@(LN0I_48KK`"`H$L$8QB?*"6A&"@MH":( 341 | M@@RO2K[;Z16#`RQ.Q"DZ`9U7@JC9(3Y:H#F#8C,.F1C=8B1K%#.JA2(& 344 | M,SC0#$`)88.3)$R=4BA+TJ 351 | M02))2Q+"68]@$X+UR+6@U(006')$:-0BC`TL::(T6R'!@`E[,T*DJ1$E7`L# 352 | M9HS]4.%%,`0CAKH$"9--.AD5ZC(-R1&G0B(HWOEER)(C2I;Z\OE5JE'K/6\! 353 | M6/(4"),G0G3#]C#$R!#?7X8(2>*$R67;5H9DSQ,(CO:@2I80R3,KMF+[MF&N2_3.QTMO)T\=.@.L\^(@AIC]L/CJ 355 | M@L;>VN6^6\K8A8S+*.OJLEG&.@LL&B&%AF:8P:B*@DNAR8H\2CZYY2'"2*"$ 356 | M@M,A((@:FBZC"F?`$2*J*,J.*!F0.JH:(Z+!<:N""M*J2:=6W/HA@Z2L&B6. 357 | M&$+*>(\,TGY"$A!"L,2+"C*(H9P<0CJB')O.8L65/"+`FH4"*KIFAB"LP"*, 358 | M)/+B)H<@'!XYG7)NNJ.J`G;X[(H!1%@0$AC+#_R*&0 359 | M:V8((U882KG(EADB'B*#O!;)(9*#S=X4A$@>$86?&:+*^(A*D=6G:;+:L,JM 360 | MJC#"02!)#!`C&P`8HPX54B$+IV1BP]2&BO=`H:`&7.Q`HIVZ++0NC 361 | MA06M>]^F8C#8+GHS"*.&BKTEMP,!Q##`)8G>$,/6$R(-J-*YKL@IAI+3376! 362 | M4CHM2*)]4L@AW5H;1AK=[6#U.3\4(AT,8Y0QG8T`,21P]-BTKAG%CG@+"*&$ 363 | MM.#`3HHH@JGTW+TA.2#D#L*IH(`,@HG!C28T:$"#(#0A0A/<0E&TT$90\[1@ 364 | MK]Z3S";VS)-#@+`D+:""\C)"H:(WRBBTL#*2$$B.-DKH`HO(.LB$W6(#C<'4 365 | M:!Q-##B!GBNC-&A+T\.,T$XK15EN[]J/+<<^490C[N[:![4/ZBBA@CKRNS?@ 366 | M6-)(=;<$FBV,#\$Q"*$I?@O.Q[`>"@UH)G9)$#B.,E)3R'ILWRL-7%:+O;0M 367 | MJ]Z'1HV*JB(8?8/SI=V]IO8!H!K0DA`XOP;"*/2BD>/(0?MH0),@G)YPY>RV 368 | MDF+3PY"%V-8(=6^AW?6P6$N\Z-YQG;N#;"7/FAZ,8`Z"U]0'9:BY6"\,@5C8 371 | MI<#[&--56SKSIHXI;JF@ 375 | MV=`'[$L'QZR3"C>HUL]^373DW.`J**09LI$'L3S<0!R*T2+)FDD=!!.72.FE 376 | MW'DVLPYDZ1U[X8<>L&F&4OIS!8*TQ!7<-H4@9&23>'9"$XS6M;7NF9D/*/D^ 377 | M*6*+H5>EEW?99,$(*8FI!6/APHQX1@-F\"IC"U5@618HEZ&3C;OC"KZ\Y2,1 378 | MX2A4+IRIBWPGR8D%*#9%"Z*9I":]9"4IC#T*IPU04`Y,H`1-$2'GHC!I(!MI 379 | M+#EE2DYO8U$;C`ZQG\.S*.DLLS9ZN2Z^C2!N4XA#'BJ0=PNAH``N:T$9L-6N 380 | M+A6GJ4.6V@U"0@#S4:5%:GQ';OZ%F;;5M 383 | MEA7("#^U6H6P0CL&V6KKKMP4*V_YV^,8QSG!DQ4LLDMT7U%7=+'3'=(86$-F 384 | MQ]1J3AP=HBY$1)&TKI2G?+&(;"3I:7H(A3\PI@<3F(+048A4I_'1@&`_C0UV 385 | M-_?77Q&+1HYC:"L$W9G>T0 386 | MVJ/#H$L%^T`")B7'%2HVE6,2>;!.VF9`@3%P'6RL3D6$++<*&,8;"M!?E3LHX7-?3-S*.NK 388 | ML%WSA_`+-UO*ZPS*BLDY].$>]M32J>E&):EM=`[:DSHP)2Z/%`W>/B`?.\W,'((\!#=E 393 | MB4#7_4E!7Y-8)SLIN\%XYR:ND+`I).1'MH+J&I(3#P]WSL1G[Y=%**@Y;M/(.3#"TUNZY>]O!/.>G8W0`DB!8=$IW6W:"U?Y>+#G0Q4"NYR 396 | MZ5WN"D6YTM6O=I7=7;:G=ZE>TV(F^O9`N,V0@#*$!WXJP-X<"F#N<]>VUM^K 397 | M7_R:[(/$62$VG.X-[X'3O5:GJ4(@R%)=%""`F%SH><#M=O#ID'_F>HYQW5-` 398 | M@QOM.4QV3=#HL!;BL@\?^*./E1@,:?8-JU*B)3\%W=1W2GOIT6;H#P?,C;?PT2H"%3!GIZD=1#M"O 400 | M,6Z?WT_GK[^H+W&#&:,!>"KU.273<2=.^YW',0)?"KSU^QL1>Q9/\Z?_^S?( 401 | MR(9R^0THV9J]H#X6<8%GNR(0NS(^N*H?22/(H`H:NH!X2TN@L"9W$"5W.:=U 402 | M^'/`X%[`!:,8BH!P^KL-*0>=^+&7B883!XP<0*'-P,AA:\I9: 403 | M:K/JTY8&VJMZB#EP&0P0@3(0"0(?!@5O&G_P^PKO,Y;R."#14:A/>8L3*1Z_ 404 | MARBR69[Q^2<0@9HS!0P5XS@/A2;(A%OH!;#1H"X5H85X<58$A8G%H3JR6(1! 405 | M``FFNTQ8>2O=V:3KAKY)"Q$-$@>PZ:50Z+50ZZUCJWK(BE(HF9W\*X^?*YW" 406 | MFWG$!ZNX^`2(L2D2@@S!.[&)!2>%N$"P2UJT.Z5+:48`1D<8>%0TN84Y69WX 407 | MZ(N+2ZWUN@SIXGV(.31:.JP#>I>C"D>,L&[,25K8J%K4@38(`IV\@3(9*:E" 408 | M1J]222(>!A&H2)=EZ#"WVS&>X)FEH#>.XV2`Q;5:*)-SN;0SR[LC"D]46G]L 409 | M\7O\$[%_&J/S\FM<2QQM2#;6\01/)2`0.RH?QB=9@C(H@BB(@I:@L@8(V1DR 410 | M8"?]1SC:)SRH6R'&L)BY,38PEF"(P+VLJ^D^3YGN8EM@4R*I!'(TJ,AQ&"D&0T0`4GH 413 | MT$9R`H/`)Y*UD2;=@1RNFE9H\"=P"+MBF#EON5OJ,H#U@V",8&;&NJ6EJ6P, 414 | M9(H.(2W``P%P@Q;"J1XHX$=:4F@F@V0T"Z#1``F^D&4T`0/!IV_R)B+("7KR 415 | MIP$R9`/(('G*IC.JIS6[93(1IXN@8FR`IK\L9"#/T$MRNFKZ&0'NYD+TI'1*I%;0LB81\XD`T6,\8"=V8R]3H#HH 417 | M@:9UYXUFVC$QBWS>8P^?R,C)#S("'PA@&@AR9\`H1_9@*9XY0[="UL`%EY:*BT680E(^'>18@_*Y@N5 419 | M,$*!\+[_@:ALJ'36N1-MY@NP2%%A@#0P25'RNW?AZ6#1(*E((0-F@TA&VS%I 420 | M@$C2LX<^*C!?%SI=%&(AEZ(1L1W?"+KU:S2PE+S*%:9CE'[))UYV%AYN%'6^ 421 | M,*GW.WSA%8]A%68)>'`AEDOAE6UJNW5>903A$Y/P9U)Y:JEL@8)C(!!18`RI 422 | ME">+@F!YRG]18&F*`*/16VA>T8'_`6=)!R#(@Q!(\:#(A[*YV@W*JQ$(*CO: 423 | M2Z_/FUN49R975"P9,(M[DY$+A(8P,S9P!(!P5',,J5AYBES58Q:IX+VOZ7;P-XD!Q$.3F$WPHKRW>7C3TT:3`$M^ 428 | M-`DY\"FP!S]ZRH(/J7K-IE08.'Q%%+_&%%^%45@9JQIK$UT!Y:=!\T[Z\83U 429 | M8Z=#:AZSMSXZ.GR%6+>]U(UK\&_-:FD^YROKMBZIV$OY2ZL'RH*1L!&(2`A) 430 | MXR[I&0)7X0GOAWA<1I>R2"9)`Y,.ME\Q9`TP9K`5V:TS 433 | MB7S=VUK^@UP!:IYZ,UW7)!*;RY25VI26>3.?^W*H1SJ-`4?]":7>@'BH1+(\ 434 | M9IM4R)DW>[R-QA3-<`[G]31Z@(9,N9V[)8H1](&1,'LOHJMN<;RON?<0#B(HH8O'E40Q&<'@93`CE!(PEDJ+EEI]BGPIE79K 437 | M\^\(E.M(C$#B61D?#EYRLQK`#H/]THQ&(VHQ@PS=6A(0VQOV#0//!Z[(C:E^ 438 | MRB/'9"Q3C$X91(DY2UP-]_$P@+"@L/1H(/&XFW\5=QO&CAM])=($`H&1L&'* 439 | M$%LJ8%?^2E/^`DW@%`!KH7<-RXAT(T/)(!%&Y^*DB80A\,:0WDT)-#S^"T 441 | MSS`RC,;TF=G45@`?4A.(@2QW3TDR,&Z]H.[,SCQM5IG:9QDP62$=%9-^("EC 442 | M8WSP=PVH@*9(I#+3`X?P%S1MSGFH)$JU"B]P3"[_8LG,D>:\@^P0S9`TSM<( 443 | M)MM\!<0,38/^)1\&9N<"!2YGS+^@^GH`SI>@M)O##PQV@L8DF\:DLD>ANK@6 444 | M%T3!8`W(BK98+%ZE$2S"@US<`^63#X_J$[.\B&SF*2LYMAJ!@/=90(/+0*QJ9G+8`G(924Y 447 | M@O1:YE)"A=0ZJ>Z9B5"88R,&%W_8"HP 448 | MS>>&LM+H+Q;:KWVRCSH"$0F5I`.H4FC:RSN?QG@56BJ*6\KFX$3J&9QZB@Q!/,`(6[9G`TT:&XIJW 451 | M3@2RQ9NW9FB\;0>[;7=65M1V@STLU: 453 | M"1".88Q.+Y6YSZY;)Y.5"%H?T\TQH$?^8K;J[YUIH2QBXMLR@;)5!!_EG-C6 454 | M5"WI'=X1[8:]GY`@>\X8I2L?'A7&K,")\C#N:GXXE 455 | M;555=/R9)A,=G$5F!']5>L\9'S?=H`(K8ZS('W;?GSV*GV/'L]9@CI2`'OV3BR16P1L]S-CCS[J_1!WJQX:Q>' 459 | M.SR($0GN64LI3",'KTCJ^Y`]-I##7T]>8/.(37MS7MM=&EP[WZ.6WKX2)`8( 460 | MZ_8U;!0WY!%(:MALR((6,FBR3=OR3,Y#3GL^>?B#3,T<"M_V'61G((.'2]P8 461 | MHE\>\?T*%L!A.Z[G[0K_83.XN@(5LIJ6G\,`%KM4L[[P'= 462 | M!90L6+^S/E,X`]<&\^OS&=$&`+^VOX@^:;Z,V,MS,MU]#KC\/9&Q]^8=6;`U 463 | M6U#&$GE(U,Y61FF,;:T,-/6HJ0D"29VQW]^KH\(=F$F5D''5]$; 464 | MY9K%GM6%'>SXO>$Y7<&;4C<9'.,8-;Q&OP6RO0UB9>S;=VM48#KFDO2O-Y[/=&;#]=;\OU'OCHVQ>HF7.R9YE528^R'N,A71E)!?.97 466 | MQ_BR2-_F6S!MJMYKD64L#9'1"#93=IWL7(J,G*_-ON?X-SJ*';ONO@_*5(@, 467 | M3]B'@M"'(Y>74.A#?7\QFO]M[6^:[OWZSO?%71>6G_>G[([E:O=UW 468 | MJ`,EJY7\%J>BR&^5F[O2OA^+LW*?_;US_(LP_^+J$)N)%2>(F]@]F7$'N;T[ 469 | M&@*(F]B_N=5I-^0;)"P=G7[3R-X[O/.G+ZF%?T;"TM&WF"?&@KB)R<)YT,+% 470 | M&\.FJ:F)L?8[_96X`@E0`QF0&@4BBK,3T8R;N"-GW1&^-21CI5>/C7A/Q-B5 471 | M9L:UC"TWW!&^-21C`1%0]]B(]X1:TA7=&/_.-&6DTQ=GT>_`'!EGW1&^-21C 472 | M`1&0]=B(]P1C+4W<%1M]T'U(7L7>:<\=X5M#,A80`6F/C7A/,-;2Q%W$%AM] 473 | M1"5G7<7>K92JMS1=K48'RH^@,!8]I@:;80S(^KV8MI8LK-AN'K7?_S4B8C37 474 | M6Q#=(*:\ZQ`F#@6M@ZC:D1%9'#7(G,A)&L!)O)&ETPL>T@V(@!#!,T)K";HE 475 | MM9:@9W-N'%OILAS=@`@('7AV!.!1@XR@#.WF?6N\4:[\7WKP 477 | M+/5H++L=E43Z`?4B+7?Z2=]RY\%RB?2P(3;+G<0)S$8*.$L64DXBRR'_@EP6 478 | M4H[27K[U9R$HT0IZ9"'E_,R1Q#L/+6C2.O6(:W<6@A*MC))[QUM//!%H@X"$ 479 | MX`ZR!2!1@#CV"O=`RL;%&X,Q41B(8R^HC2-MUV3S2B,YKW%-`G-UDAXYKW$\ 480 | MO(<"A%%$TJ1?^U8N1I]_3R*J2]Q3KU9X\6=ZA8[UXSB1W-\[GU^$DC3B3W;6 481 | M`)OP2RWQ>:4^F`F2!LC3>%:./TWP\`C($2"(!33_3B*J>S`YJX(TZH.9D,4D 482 | MY@!GD9\86_4DHKH_<*.UQAC[D!(KJC2P"3]GD3"JK2KN.)%#\PS9W*8JW!3O 483 | M6S]+1V(X^EY$/XI^NXR;X$[WAC4W..'V1@&_D_.4=']TXCB1O&6,L0])DX#@H?2^/@!P!@EA`ZRA(#KX'DS/B`M?N!B".@Q(>XOC@ 485 | M

L"RL!E#.-2T\PS9 486 | M7.C21(3O9:24WD)\8@QQ- 490 | M=<^5@])5/O)F,C6ZUB?.XQO%X#SVU9/PWS6[HFH#.E(#1!X34P"[6ILQ4LW= 491 | MB%76W,6(]X1*=XS*C<3"U=C=O-*H"Q6-T1V-T83RJY?\S:5!"G\A]$2-#JU@ 492 | M9%/?;9Z:T!A-H"*"L>W%U16&\#[0Y5$3DAAVX9)#8RQA``%;L0Z)(172.-/% 493 | M&P/O`?4T" 494 | M7UQ=92WS1VQ33Q.-`=O%Y5$3RI-).=/%8Q\0@1(0`?4TT1BP7UQ=`0FP\$U" 495 | M`O4T]=7%Y5$3XI(S7;PQ(`=03Y,73H2'2`9VS@"8(@1`:I@(H@;B!@V06 496 | MD@4"`0/P3@D0``OP(B:S(1&2`@%P`&\6%APN@3#P9N$2(SDP`B]B(A80`"OP 497 | M$@K\`1@0``?P5BPH,P\N@0CP#@.2`Q/P9B$A`1OP`L5A,`]0XA((`F_%@K)N 498 | M(!`P`V_%DAX0`#OP`L5AZ"LN@1#P`L5A2&"675Q=`0F!`@V&S`Z0``NP`@V& 499 | MT-?%&P.(I#^&``?0``F!`@V&3/7%&P.^`(W_`PV0``NP`DW6TM?%&P.(I#^& 500 | M``?0``F!`@V&3/7%&P.!`@V&S`Z0``NP`DW6TM?%&P.(I#^&``?0``F!`@V& 501 | M3/7%&P.!`@V&S`Z0``NP`@V0@!!]7;PQ@$CZ8PAP``V0$"C08,A47[PQ8`;0 502 | M^#_0``FP``O09"U]7;PQ@$CZ,PX(`PV0``NP``V&T/;%&X/$`HW_`PV0``NP 503 | M`$W6TM?%&P.(I#_C@##0``FP``O0H+9H7+PQ8#K0^#_0``FP``O09"U]7;PQ 504 | M@$CZ8PAP``V0$"C08,A47[PQ8#K0^#_0``FP``O09"U]7;PQ@$CZ8PAP``V0 505 | M$"C08,A47[PQ`-OLW"K0``FP`P?0B!_:.OQL7UQ=84CZ&R/?,`[?,(Z@7+PQ 506 | M8`;08$CZ`PFP`B?0`$D9T,+%&T.]`0UJSP[C`!O0``FPB,;%&P/R`@UJD\6- 507 | MF\_%U94Q@C#0&"-P`HTQTNW%&P,P`3/0&"-P`(TQDN:-2QHJ7+PQ`"MP`@V& 508 | MI#^0`*ML7+PQ;!K0&".P``V&H*[#%^K%U57$@C#0,`[S1RP(`XTQTM?%&P.P 509 | M@@C0,,Z>1^R>BY_$7;PQ5";0,`[S1RR(`(TQTM?%&T-E`@WC['G$@@C0B)_$ 510 | M7;PQ8`;0&"-P`DW$`B?0B!^?7+PQ`!MP`HWX`0?01"QP`HTQTM?%&\.F`0V0 511 | M`"LP`TW$[KDQTM?%&\.F`0V0`"LP`TW$`B?0B)_$7;PQ8`;01"SS`PFP[CF0 512 | M`*L47;PQD#W0``FP`2?0&".P`(TQ9O#%&P.%`(WX@2C0B!]P`(WX\?[%&P,P 513 | M`2?08$CZ`PFP@S#08`AM7[PQ`".P`@UJ@S#0``FP@0C0H#;=7KPQD#W0``FP 514 | M`2?0&"-P`@V0`"M]7;PQ``>P`,VM`DW6`@O01*S$7;PQD#W0``FP`2?0&",P 515 | M`XWXH6[!7EQ=`0E5`PV0`-OL``FP,C^0`(ML7+PQ`!.P`0V0`-OLH#:P``WC 516 | MR,;%&P.P`@O0``FP``O0H!1P``V0`&L&7[PQ`!MP``V0``NP``V0%"O0H'8& 517 | M7[PQ8`;0``EI``V&``?0,$X&7[PQ`#NP``V0D`;0&"-P``V0`!N?7+PQ`#NP 518 | M``VI`0O0H!0(`PV0`(MH7+PQ`/OLD-KLB!^P``WCB,;%&P-F`(WX,;_XS8[X 519 | M"-2%FE3R(@X38#% 523 | MY5$3`@E0`QF0^I2V"XCUHB*"(1##69B:B-;%<7RCM3?6EHHUE0I7M7>IQ`A? 524 | M`RS0<7RCJ1G(EXH3X'2I:!0(E8J$LB7(\C7``AW'-YJ$LB4HE@PI?`RS0<7RCM3?6EHJ2EXIB\C62IR6H4M&.X1KU@C)HD@P-R2WV[-G% 526 | MU57$IC^&,#^&``?08"B2FQ;I(2S]9!CU@C)HDHRL[E7%7EQ=80BP``V&`#/0 527 | M8`@P`PVF^1TC40G0<7RC,1734'?77QS'-QJM-"8"```````````````````` 528 | 3`````````````````````````' 529 | ` 530 | end 531 | 532 | -------------------------------------------------------------------------------- /supermon64.asm: -------------------------------------------------------------------------------- 1 | ; ******************************** 2 | ; * SUPERMON+ 64 JIM BUTTERFIELD * 3 | ; * V1.2 AUGUST 20 1985 * 4 | ; ******************************** 5 | 6 | ; Reformatted and annotated in late 2016/early 2017 by J.B. Langston. 7 | ; 8 | ; I've made the minimum necessary changes to this code to get it to assemble 9 | ; with 64tass. Specifically, I changed the following directives from PAL 10 | ; that 64tass doesn't support: 11 | ; - .ASC => .TEXT 12 | ; - *=*+X => .FILL X 13 | ; 14 | ; Aside from this, I have adopted a strict whitespace and comments only 15 | ; policy so that I preserve code exactly as Jim Butterfield wrote it. 16 | ; 17 | ; I think my comments are correct but I don't guarantee I haven't made 18 | ; any errors. Sadly Jim isn't around to ask anymore. If you spot any 19 | ; misunderstanings or errors in my comments, please report them. 20 | 21 | ; ----------------------------------------------------------------------------- 22 | ; temporary pointers 23 | TMP0 = $C1 ; used to return input, often holds end address 24 | TMP2 = $C3 ; usually holds start address 25 | 26 | ; ----------------------------------------------------------------------------- 27 | ; kernal variables 28 | SATUS = $90 ; kernal i/o status word 29 | FNLEN = $B7 ; length of current filename 30 | SADD = $B9 ; current secondary address (official name SA) 31 | FA = $BA ; current device number 32 | FNADR = $BB ; pointer to current filename 33 | NDX = $C6 ; number of characters in keyboard buffer 34 | KEYD = $0277 ; keyboard buffer 35 | BKVEC = $0316 ; BRK instruction vector (official name CBINV) 36 | 37 | *= $0100 ; store variables in tape error buffer 38 | 39 | ; ----------------------------------------------------------------------------- 40 | ; variables 41 | ACMD .FILL 1 ; addressing command 42 | LENGTH .FILL 1 ; length of operand 43 | MNEMW .FILL 3 ; 3 letter mnemonic buffer 44 | SAVX .FILL 1 ; 1 byte temp storage, often to save X register 45 | OPCODE .FILL 1 ; current opcode for assembler/disassembler 46 | UPFLG .FILL 1 ; flag: count up (bit 7 clear) or down (bit 7 set) 47 | DIGCNT .FILL 1 ; digit count 48 | INDIG .FILL 1 ; numeric value of single digit 49 | NUMBIT .FILL 1 ; numeric base of input 50 | STASH .FILL 2 ; 2-byte temp storage 51 | U0AA0 .FILL 10 ; work buffer 52 | U0AAE =* ; end of work buffer 53 | STAGE .FILL 30 ; staging buffer for filename, search, etc. 54 | ESTAGE =* ; end of staging buffer 55 | 56 | *= $0200 ; store more variables in basic line editor buffer 57 | 58 | INBUFF .FILL 40 ; 40-character input buffer 59 | ENDIN =* ; end of input buffer 60 | 61 | ; the next 7 locations are used to store the registers when 62 | ; entering the monitor and restore them when exiting. 63 | 64 | PCH .FILL 1 ; program counter high byte 65 | PCL .FILL 1 ; program counter low byte 66 | SR .FILL 1 ; status register 67 | ACC .FILL 1 ; accumulator 68 | XR .FILL 1 ; X register 69 | YR .FILL 1 ; Y register 70 | SP .FILL 1 ; stack pointer 71 | 72 | STORE .FILL 2 ; 2-byte temp storage 73 | CHRPNT .FILL 1 ; current position in input buffer 74 | SAVY .FILL 1 ; temp storage, often to save Y register 75 | U9F .FILL 1 ; index into assembler work buffer 76 | 77 | ; ----------------------------------------------------------------------------- 78 | ; kernal entry points 79 | SETMSG = $FF90 ; set kernel message control flag 80 | SECOND = $FF93 ; set secondary address after LISTEN 81 | TKSA = $FF96 ; send secondary address after TALK 82 | LISTEN = $FFB1 ; command serial bus device to LISTEN 83 | TALK = $FFB4 ; command serial bus device to TALK 84 | SETLFS = $FFBA ; set logical file parameters 85 | SETNAM = $FFBD ; set filename 86 | ACPTR = $FFA5 ; input byte from serial bus 87 | CIOUT = $FFA8 ; output byte to serial bus 88 | UNTLK = $FFAB ; command serial bus device to UNTALK 89 | UNLSN = $FFAE ; command serial bus device to UNLISTEN 90 | CHKIN = $FFC6 ; define input channel 91 | CLRCHN = $FFCC ; restore default devices 92 | INPUT = $FFCF ; input a character (official name CHRIN) 93 | CHROUT = $FFD2 ; output a character 94 | LOAD = $FFD5 ; load from device 95 | SAVE = $FFD8 ; save to device 96 | STOP = $FFE1 ; check the STOP key 97 | GETIN = $FFE4 ; get a character 98 | 99 | ; ----------------------------------------------------------------------------- 100 | ; set up origin 101 | 102 | .WEAK 103 | ORG = $9519 104 | .ENDWEAK 105 | 106 | * = ORG 107 | 108 | ; ----------------------------------------------------------------------------- 109 | ; initial entry point 110 | SUPER LDY #MSG4-MSGBAS ; display "..SYS " 111 | JSR SNDMSG 112 | LDA SUPAD ; store entry point address in tmp0 113 | STA TMP0 114 | LDA SUPAD+1 115 | STA TMP0+1 116 | JSR CVTDEC ; convert address to decimal 117 | LDA #0 118 | LDX #6 119 | LDY #3 120 | JSR NMPRNT ; print entry point address 121 | JSR CRLF 122 | LDA LINKAD ; set BRK vector 123 | STA BKVEC 124 | LDA LINKAD+1 125 | STA BKVEC+1 126 | LDA #$80 ; disable kernel control messages 127 | JSR SETMSG ; and enable error messages 128 | BRK 129 | 130 | ; ----------------------------------------------------------------------------- 131 | ; BRK handler 132 | BREAK LDX #$05 ; pull registers off the stack 133 | BSTACK PLA ; order: Y,X,A,SR,PCL,PCH 134 | STA PCH,X ; store in memory 135 | DEX 136 | BPL BSTACK 137 | CLD ; disable bcd mode 138 | TSX ; store stack pointer in memory 139 | STX SP 140 | CLI ; enable interupts 141 | 142 | ; ----------------------------------------------------------------------------- 143 | ; display registers [R] 144 | DSPLYR LDY #MSG2-MSGBAS ; display headers 145 | JSR SNDCLR 146 | LDA #$3B ; prefix registers with "; " to allow editing 147 | JSR CHROUT 148 | LDA #$20 149 | JSR CHROUT 150 | LDA PCH ; print 2-byte program counter 151 | JSR WRTWO 152 | LDY #1 ; start 1 byte after PC high byte 153 | DISJ LDA PCH,Y ; loop through rest of the registers 154 | JSR WRBYTE ; print 1-byte register value 155 | INY 156 | CPY #7 ; there are a total of 5 registers to print 157 | BCC DISJ 158 | 159 | ; ----------------------------------------------------------------------------- 160 | ; main loop 161 | STRT JSR CRLF ; new line 162 | LDX #0 ; point at start of input buffer 163 | STX CHRPNT 164 | SMOVE JSR INPUT ; CHRIN kernal call to input a character 165 | STA INBUFF,X ; store in input buffer 166 | INX 167 | CPX #ENDIN-INBUFF ; error if buffer is full 168 | BCS ERROR 169 | CMP #$0D ; keep reading until CR 170 | BNE SMOVE 171 | LDA #0 ; null-terminate input buffer 172 | STA INBUFF-1,X ; (replacing the CR) 173 | ST1 JSR GETCHR ; get a character from the buffer 174 | BEQ STRT ; start over if buffer is empty 175 | CMP #$20 ; skip leading spaces 176 | BEQ ST1 177 | S0 LDX #KEYTOP-KEYW ; loop through valid command characters 178 | S1 CMP KEYW,X ; see if input character matches 179 | BEQ S2 ; command matched, dispatch it 180 | DEX ; no match, check next command 181 | BPL S1 ; keep trying until we've checked them all 182 | ; then fall through to error handler 183 | 184 | ; ----------------------------------------------------------------------------- 185 | ; handle error 186 | ERROR LDY #MSG3-MSGBAS ; display "?" to indicate error and go to new line 187 | JSR SNDMSG 188 | JMP STRT ; back to main loop 189 | 190 | ; ----------------------------------------------------------------------------- 191 | ; dispatch command 192 | S2 CPX #$13 ; last 3 commands in table are load/save/validate 193 | BCS LSV ; which are handled by the same subroutine 194 | CPX #$0F ; next 4 commands are base conversions 195 | BCS CNVLNK ; which are handled by the same subroutine 196 | TXA ; remaining commands dispatch through vector table 197 | ASL A ; multiply index of command by 2 198 | TAX ; since table contains 2-byte addresses 199 | LDA KADDR+1,X ; push address from vector table onto stack 200 | PHA ; so that the RTS from GETPAR will jump there 201 | LDA KADDR,X 202 | PHA 203 | JMP GETPAR ; get the first parameter for the command 204 | LSV STA SAVY ; handle load/save/validate 205 | JMP LD 206 | CNVLNK JMP CONVRT ; handle base conversion 207 | 208 | ; ----------------------------------------------------------------------------- 209 | ; exit monitor [X] 210 | EXIT JMP ($A002) ; jump to warm-start vector to reinitialize BASIC 211 | 212 | ; ----------------------------------------------------------------------------- 213 | ; display memory [M] 214 | DSPLYM BCS DSPM11 ; start from previous end addr if no address given 215 | JSR COPY12 ; save start address in TMP2 216 | JSR GETPAR ; get end address in TMP0 217 | BCC DSMNEW ; did user specify one? 218 | DSPM11 LDA #$0B ; if not, show 12 lines by default 219 | STA TMP0 220 | BNE DSPBYT ; always true, but BNE uses 1 byte less than JMP 221 | DSMNEW JSR SUB12 ; end addr given, calc bytes between start and end 222 | BCC MERROR ; error if start is after end 223 | LDX #3 ; divide by 8 (shift right 3 times) 224 | DSPM01 LSR TMP0+1 225 | ROR TMP0 226 | DEX 227 | BNE DSPM01 228 | DSPBYT JSR STOP ; check for stop key 229 | BEQ DSPMX ; exit early if pressed 230 | JSR DISPMEM ; display 1 line containing 8 bytes 231 | LDA #8 ; increase start address by 8 bytes 232 | JSR BUMPAD2 233 | JSR SUBA1 ; decrement line counter 234 | BCS DSPBYT ; show another line until it's < 0 235 | DSPMX JMP STRT ; back to main loop 236 | MERROR JMP ERROR ; handle error 237 | 238 | ; ----------------------------------------------------------------------------- 239 | ; alter registers [;] 240 | ALTR JSR COPY1P ; store first parameter in PC 241 | LDY #0 ; init counter 242 | ALTR1 JSR GETPAR ; get value for next register 243 | BCS ALTRX ; exit early if no more values given 244 | LDA TMP0 ; store in memory, offset from SR 245 | STA SR,Y ; these locations will be transferred to the 246 | INY ; actual registers before exiting the monitor 247 | CPY #$05 ; have we updated all 5 yet? 248 | BCC ALTR1 ; if not, get next 249 | ALTRX JMP STRT ; back to main loop 250 | 251 | ; ----------------------------------------------------------------------------- 252 | ; alter memory [>] 253 | ALTM BCS ALTMX ; exit if no parameter provided 254 | JSR COPY12 ; copy parameter to start address 255 | LDY #0 256 | ALTM1 JSR GETPAR ; get value for next byte of memory 257 | BCS ALTMX ; if none given, exit early 258 | LDA TMP0 ; poke value into memory at start address + Y 259 | STA (TMP2),Y 260 | INY ; next byte 261 | CPY #8 ; have we read 8 bytes yet? 262 | BCC ALTM1 ; if not, read the next one 263 | ALTMX LDA #$91 ; move cursor up 264 | JSR CHROUT 265 | JSR DISPMEM ; re-display line to make ascii match hex 266 | JMP STRT ; back to main loop 267 | 268 | ; ----------------------------------------------------------------------------- 269 | ; goto (run) [G] 270 | GOTO LDX SP ; load stack pointer from memory 271 | TXS ; save in SP register 272 | GOTO2 JSR COPY1P ; copy provided address to PC 273 | SEI ; disable interrupts 274 | LDA PCH ; push PC high byte on stack 275 | PHA 276 | LDA PCL ; push PC low byte on stack 277 | PHA 278 | LDA SR ; push status byte on stack 279 | PHA 280 | LDA ACC ; load accumulator from memory 281 | LDX XR ; load X from memory 282 | LDY YR ; load Y from memory 283 | RTI ; return from interrupt (pops PC and SR) 284 | 285 | ; jump to subroutine [J] 286 | JSUB LDX SP ; load stack pointer from memory 287 | TXS ; save value in SP register 288 | JSR GOTO2 ; same as goto command 289 | STY YR ; save Y to memory 290 | STX XR ; save X to memory 291 | STA ACC ; save accumulator to memory 292 | PHP ; push processor status on stack 293 | PLA ; pull processor status into A 294 | STA SR ; save processor status to memory 295 | JMP DSPLYR ; display registers 296 | 297 | ; ----------------------------------------------------------------------------- 298 | ; display 8 bytes of memory 299 | DISPMEM JSR CRLF ; new line 300 | LDA #">" ; prefix > so memory can be edited in place 301 | JSR CHROUT 302 | JSR SHOWAD ; show address of first byte on line 303 | LDY #0 304 | BEQ DMEMGO ; SHOWAD already printed a space after the address 305 | DMEMLP JSR SPACE ; print space between bytes 306 | DMEMGO LDA (TMP2),Y ; load byte from start address + Y 307 | JSR WRTWO ; output hex digits for byte 308 | INY ; next byte 309 | CPY #8 ; have we output 8 bytes yet? 310 | BCC DMEMLP ; if not, output next byte 311 | LDY #MSG5-MSGBAS ; if so, output : and turn on reverse video 312 | JSR SNDMSG ; before displaying ascii representation 313 | LDY #0 ; back to first byte in line 314 | DCHAR LDA (TMP2),Y ; load byte at start address + Y 315 | TAX ; stash in X 316 | AND #$BF ; clear 6th bit 317 | CMP #$22 ; is it a quote (")? 318 | BEQ DDOT ; if so, print . instead 319 | TXA ; if not, restore character 320 | AND #$7F ; clear top bit 321 | CMP #$20 ; is it a printable character (>= $20)? 322 | TXA ; restore character 323 | BCS DCHROK ; if printable, output character 324 | DDOT LDA #$2E ; if not, output '.' instaed 325 | DCHROK JSR CHROUT 326 | INY ; next byte 327 | CPY #8 ; have we output 8 bytes yet? 328 | BCC DCHAR ; if not, output next byte 329 | RTS 330 | 331 | ; ----------------------------------------------------------------------------- 332 | ; compare memory [C] 333 | COMPAR LDA #0 ; bit 7 clear signals compare 334 | .BYTE $2C ; absolute BIT opcode consumes next word (LDA #$80) 335 | 336 | ; transfer memory [T] 337 | TRANS LDA #$80 ; bit 7 set signals transfer 338 | STA SAVY ; save compare/transfer flag in SAVY 339 | LDA #0 ; assume we're counting up (bit 7 clear) 340 | STA UPFLG ; save direction flag 341 | JSR GETDIF ; get two addresses and calculate difference 342 | ; TMP2 = source start 343 | ; STASH = source end 344 | ; STORE = length 345 | BCS TERROR ; carry set indicates error 346 | JSR GETPAR ; get destination address in TMP0 347 | BCC TOKAY ; carry set indicates error 348 | TERROR JMP ERROR ; handle error 349 | TOKAY BIT SAVY ; transfer or compare? 350 | BPL COMPAR1 ; high bit clear indicates compare 351 | LDA TMP2 ; if it's a transfer, we must take steps 352 | CMP TMP0 ; to avoid overwriting the source bytes before 353 | LDA TMP2+1 ; they have been transferred 354 | SBC TMP0+1 ; compare source (TMP2) to destination (TMP0) 355 | BCS COMPAR1 ; and count up if source is before than desitnation 356 | LDA STORE ; otherwise, start at end and count down... 357 | ADC TMP0 ; add length (STORE) to desintation (TMP0) 358 | STA TMP0 ; to calculate end of destination 359 | LDA STORE+1 360 | ADC TMP0+1 361 | STA TMP0+1 362 | LDX #1 ; change source pointer from beginning to end 363 | TDOWN LDA STASH,X ; TMP2 = source end (STASH) 364 | STA TMP2,X 365 | DEX 366 | BPL TDOWN 367 | LDA #$80 ; high bit set in UPFLG means count down 368 | STA UPFLG 369 | COMPAR1 JSR CRLF ; new line 370 | LDY #0 ; no offset from pointer 371 | TCLOOP JSR STOP ; check for stop key 372 | BEQ TEXIT ; exit if pressed 373 | LDA (TMP2),Y ; load byte from source 374 | BIT SAVY ; transfer or compare? 375 | BPL COMPAR2 ; skip store if comparing 376 | STA (TMP0),Y ; otherwise, store in destination 377 | COMPAR2 CMP (TMP0),Y ; compare to destination 378 | BEQ TMVAD ; don't show address if equal 379 | JSR SHOWAD ; show address 380 | TMVAD BIT UPFLG ; counting up or down? 381 | BMI TDECAD ; high bit set means we're counting down 382 | INC TMP0 ; increment destination low byte 383 | BNE TINCOK 384 | INC TMP0+1 ; carry to high byte if necessary 385 | BNE TINCOK 386 | JMP ERROR ; error if high byte overflowed 387 | TDECAD JSR SUBA1 ; decrement destination (TMP0) 388 | JSR SUB21 ; decrement source (TMP2) 389 | JMP TMOR 390 | TINCOK JSR ADDA2 ; increment source (TMP2) 391 | TMOR JSR SUB13 ; decrement length 392 | BCS TCLOOP ; loop until length is 0 393 | TEXIT JMP STRT ; back to main loop 394 | 395 | ; ----------------------------------------------------------------------------- 396 | ; hunt memory [H] 397 | HUNT JSR GETDIF ; get start (TMP2) and end (TMP0) of haystack 398 | BCS HERROR ; carry indicates error 399 | LDY #0 400 | JSR GETCHR ; get a single character 401 | CMP #"'" ; is it a single quote? 402 | BNE NOSTRH ; if not, input needle as hex bytes 403 | JSR GETCHR ; if so, input needle as string 404 | CMP #0 405 | BEQ HERROR ; error if needle isn't at least one byte 406 | HPAR STA STAGE,Y ; save char in staging area 407 | INY 408 | JSR GETCHR ; get another char 409 | BEQ HTGO ; if it's null start searching 410 | CPY #ESTAGE-STAGE ; have we filled up the needle staging area? 411 | BNE HPAR ; if not, get another character 412 | BEQ HTGO ; if so, start searching 413 | NOSTRH JSR RDPAR ; read hex bytes if string not indicated 414 | HLP LDA TMP0 ; save last read byte in staging area 415 | STA STAGE,Y 416 | INY ; get another hex byte 417 | JSR GETPAR 418 | BCS HTGO ; if there is none, start searching 419 | CPY #ESTAGE-STAGE ; have we filled up the needle staging area? 420 | BNE HLP ; if not, get another byte 421 | HTGO STY SAVY ; save length of needle 422 | JSR CRLF ; new line 423 | HSCAN LDY #0 424 | HLP3 LDA (TMP2),Y ; get first byte in haystack 425 | CMP STAGE,Y ; compare it to first byte of needle 426 | BNE HNOFT ; if it doesn't match, we haven't found anything 427 | INY ; if it does, check the next byte 428 | CPY SAVY ; have we reached the end of the needle? 429 | BNE HLP3 ; if not, keep comparing bytes 430 | JSR SHOWAD ; match found, show address 431 | HNOFT JSR STOP ; no match, check for stop key 432 | BEQ HEXIT ; exit prematurely if pressed 433 | JSR ADDA2 ; increment haystack pointer 434 | JSR SUB13 ; decrement haystack length 435 | BCS HSCAN ; still more haystack? keep searching 436 | HEXIT JMP STRT ; back to main loop 437 | HERROR JMP ERROR ; handle error 438 | 439 | ; ----------------------------------------------------------------------------- 440 | ; load, save, or verify [LSV] 441 | LD LDY #1 ; default to reading from tape, device #1 442 | STY FA 443 | STY SADD ; default to secondary address #1 444 | DEY 445 | STY FNLEN ; start with an empty filename 446 | STY SATUS ; clear status 447 | LDA #>STAGE ; set filename pointer to staging buffer 448 | STA FNADR+1 449 | LDA #= $E8 indicates relative addressing 627 | JSR CHEK2B ; ACMD < $E8 indicates normal addressing 628 | DEY ; consume byte 629 | BNE TRYAD ; check for 2 more digits if not zero-page 630 | TRYMOD ASL ACMD ; shift a bit out of the addressing command 631 | BCC UB4DF ; if it's zero, skip checking current character 632 | LDA CHAR1-1,X 633 | JSR CHEKOP ; otherwise first character against operand 634 | LDA CHAR2-1,X ; get second character to check 635 | BEQ UB4DF ; if it's zero, skip checking it 636 | JSR CHEKOP ; otherwise check it against hte operand 637 | UB4DF DEX ; move to next character 638 | BNE TRYIT ; repeat tests 639 | BEQ TRYBRAN 640 | TRY4B JSR CHEK2B ; check for 4 digit address placeholder 641 | JSR CHEK2B ; by checking for 2 digits twice 642 | TRYBRAN LDA STORE ; get number of bytes in assembly buffer 643 | CMP U9F ; more bytes left to check? 644 | BEQ ABRAN ; if not, we've found a match; build instruction 645 | JMP BUMPOP ; if so, this opcode doesn't match; try the next 646 | 647 | ; convert branches to relative address 648 | ABRAN LDY LENGTH ; get number of bytes in operand 649 | BEQ A1BYTE ; if none, just output the opcode 650 | LDA STORE+1 ; otherwise check the address format 651 | CMP #$9D ; is it a relative branch? 652 | BNE OBJPUT ; if not, skip relative branch calculation 653 | LDA TMP0 ; calculate the difference between the current 654 | SBC TMP2 ; address and the branch target (low byte) 655 | TAX ; save it in X 656 | LDA TMP0+1 ; borrow from the high byte if necessary 657 | SBC TMP2+1 658 | BCC ABBACK ; if result is negative, we're branching back 659 | BNE SERROR ; high bytes must be equal when branching forward 660 | CPX #$82 ; difference between low bytes must be < 130 661 | BCS SERROR ; error if the address is too far away 662 | BCC ABRANX 663 | ABBACK TAY ; when branching backward high byte of target must 664 | INY ; be 1 less than high byte of current address 665 | BNE SERROR ; if not, it's too far away 666 | CPX #$82 ; difference between low bytes must be < 130 667 | BCC SERROR ; if not, it's too far away 668 | ABRANX DEX ; adjust branch target relative to the 669 | DEX ; instruction following this one 670 | TXA 671 | LDY LENGTH ; load length of operand 672 | BNE OBJP2 ; don't use the absolute address 673 | 674 | ; assemble machine code 675 | OBJPUT LDA TMP0-1,Y ; get the operand 676 | OBJP2 STA (TMP2),Y ; store it after the opcode 677 | DEY 678 | BNE OBJPUT ; copy the other byte of operand if there is one 679 | A1BYTE LDA OPCODE ; put opcode into instruction 680 | STA (TMP2),Y 681 | JSR CRLF ; carriage return 682 | LDA #$91 ; back up one line 683 | JSR CHROUT 684 | LDY #MSG7-MSGBAS ; "A " prefix 685 | JSR SNDCLR ; clear line 686 | JSR DISLIN ; disassemble the instruction we just assembled 687 | INC LENGTH ; instruction length = operand length + 1 byte 688 | LDA LENGTH ; for the opcode 689 | JSR BUMPAD2 ; increment address by length of instruction 690 | LDA #"A" ; stuff keyboard buffer with next assemble command: 691 | STA KEYD ; "A XXXX " where XXXX is the next address 692 | LDA #" " ; after the previously assembled instruction 693 | STA KEYD+1 694 | STA KEYD+6 695 | LDA TMP2+1 ; convert high byte of next address to hex 696 | JSR ASCTWO 697 | STA KEYD+2 ; put it in the keyboard buffer 698 | STX KEYD+3 699 | LDA TMP2 ; convert low byte of next address to hex 700 | JSR ASCTWO 701 | STA KEYD+4 ; put it in the keyboard buffer 702 | STX KEYD+5 703 | LDA #7 ; set number of chars in keyboard buffer 704 | STA NDX 705 | JMP STRT ; back to main loop 706 | SERROR JMP ERROR ; handle error 707 | 708 | ; check characters in operand 709 | CHEK2B JSR CHEKOP ; check two bytes against value in accumulator 710 | CHEKOP STX SAVX ; stash X 711 | LDX U9F ; get current index into work buffer 712 | CMP U0AA0,X ; check whether this opcode matches the buffer 713 | BEQ OPOK ; matching so far, check the next criteria 714 | PLA ; didn't match, so throw away return address 715 | PLA ; on the stack because we're starting over 716 | BUMPOP INC OPCODE ; check the next opcode 717 | BEQ SERROR ; error if we tried every opcode and none fit 718 | JMP ATRYOP ; start over with new opcode 719 | OPOK INC U9F ; opcode matches so far; check the next criteria 720 | LDX SAVX ; restore X 721 | RTS 722 | 723 | ; ----------------------------------------------------------------------------- 724 | ; disassemble [D] 725 | DISASS BCS DIS0AD ; if no address was given, start from last address 726 | JSR COPY12 ; copy start address to TMP2 727 | JSR GETPAR ; get end address in TMP0 728 | BCC DIS2AD ; if one was given, skip default 729 | DIS0AD LDA #$14 ; disassemble 14 bytes by default 730 | STA TMP0 ; store length in TMP0 731 | BNE DISGO ; skip length calculation 732 | DIS2AD JSR SUB12 ; calculate number of bytes between start and end 733 | BCC DERROR ; error if end address is before start address 734 | DISGO JSR CLINE ; clear the current line 735 | JSR STOP ; check for stop key 736 | BEQ DISEXIT ; exit early if pressed 737 | JSR DSOUT1 ; output disassembly prefix ". " 738 | INC LENGTH 739 | LDA LENGTH ; add length of last instruction to start address 740 | JSR BUMPAD2 741 | LDA LENGTH ; subtract length of last inst from end address 742 | JSR SUBA2 743 | BCS DISGO 744 | DISEXIT JMP STRT ; back to mainloop 745 | DERROR JMP ERROR 746 | 747 | DSOUT1 LDA #"." ; output ". " prefix to allow edit and reassemble 748 | JSR CHROUT 749 | JSR SPACE 750 | 751 | DISLIN JSR SHOWAD ; show the address of the instruction 752 | JSR SPACE ; insert a space 753 | LDY #0 ; no offset 754 | LDA (TMP2),Y ; load operand of current instruction 755 | JSR INSTXX ; get mnemonic and addressing mode for opcode 756 | PHA ; save index into mnemonic table 757 | LDX LENGTH ; get length of operand 758 | INX ; add 1 byte for opcode 759 | DSBYT DEX ; decrement index 760 | BPL DSHEX ; show hex for byte being disassembled 761 | STY SAVY ; save index 762 | LDY #MSG8-MSGBAS ; skip 3 spaces 763 | JSR SNDMSG 764 | LDY SAVY ; restore index 765 | JMP NXBYT 766 | DSHEX LDA (TMP2),Y ; show hex for byte 767 | JSR WRBYTE 768 | 769 | NXBYT INY ; next byte 770 | CPY #3 ; have we output 3 bytes yet? 771 | BCC DSBYT ; if not, loop 772 | PLA ; restore index into mnemonic table 773 | LDX #3 ; 3 letters in mnemonic 774 | JSR PROPXX ; print mnemonic 775 | LDX #6 ; 6 possible address mode character combos 776 | PRADR1 CPX #3 ; have we checked the third combo yet? 777 | BNE PRADR3 ; if so, output the leading characters 778 | LDY LENGTH ; get the length of the operand 779 | BEQ PRADR3 ; if it's zero, there's no operand to print 780 | PRADR2 LDA ACMD ; otherwise, get the addressing mode 781 | CMP #$E8 ; check for relative addressing 782 | PHP ; save result of check 783 | LDA (TMP2),Y ; get the operand 784 | PLP ; restore result of check 785 | BCS RELAD ; handle a relative address 786 | JSR WRTWO ; output digits from address 787 | DEY 788 | BNE PRADR2 ; repeat for next byte of operand, if there is one 789 | PRADR3 ASL ACMD ; check whether addr mode uses the current char 790 | BCC PRADR4 ; if not, skip it 791 | LDA CHAR1-1,X ; look up the first char in the table 792 | JSR CHROUT ; print first char 793 | LDA CHAR2-1,X ; look up the second char in the table 794 | BEQ PRADR4 ; if there's no second character, skip it 795 | JSR CHROUT ; print second char 796 | PRADR4 DEX ; next potential address mode character 797 | BNE PRADR1 ; loop if we haven't checked them all yet 798 | RTS ; back to caller 799 | RELAD JSR UB64D ; calculate absolute address from relative 800 | CLC 801 | ADC #1 ; adjust address relative to next instruction 802 | BNE RELEND ; don't increment high byte unless we overflowed 803 | INX ; increment high byte 804 | RELEND JMP WRADDR ; print address 805 | 806 | UB64D LDX TMP2+1 ; get high byte of current address 807 | TAY ; is relative address positive or negative? 808 | BPL RELC2 ; if positive, leave high byte alone 809 | DEX ; if negative, decrement high byte 810 | RELC2 ADC TMP2 ; add relative address to low byte 811 | BCC RELC3 ; if there's no carry, we're done 812 | INX ; if there's a carry, increment the high byte 813 | RELC3 RTS 814 | 815 | ; ----------------------------------------------------------------------------- 816 | ; get opcode mode and length 817 | 818 | ; Note: the labels are different, but the code of this subroutine is almost 819 | ; identical to the INSDS2 subroutine of the Apple Mini-Assembler on page 78 of 820 | ; the Apple II Red Book. I'm not sure exactly where this code originated 821 | ; (MOS or Apple) but it's clear that this part of Supermon64 and the 822 | ; Mini-Asssembler share a common heritage. The comments showing the way the 823 | ; opcodes are transformed into indexes for the mnemonic lookup table come 824 | ; from the Mini-Assembler source. 825 | 826 | INSTXX TAY ; stash opcode in accumulator in Y for later 827 | LSR A ; is opcode even or odd? 828 | BCC IEVEN 829 | LSR A 830 | BCS ERR ; invalid opcodes XXXXXX11 831 | CMP #$22 832 | BEQ ERR ; invalid opcode 10001001 833 | AND #$07 ; mask bits to 10000XXX 834 | ORA #$80 835 | IEVEN LSR A ; LSB determines whether to use left/right nybble 836 | TAX ; get format index using remaining high bytes 837 | LDA MODE,X 838 | BCS RTMODE ; look at left or right nybble based on carry bit 839 | LSR A ; if carry = 0, use left nybble 840 | LSR A 841 | LSR A 842 | LSR A 843 | RTMODE AND #$0F ; if carry = 1, use right nybble 844 | BNE GETFMT 845 | ERR LDY #$80 ; substitute 10000000 for invalid opcodes 846 | LDA #0 847 | GETFMT TAX 848 | LDA MODE2,X ; lookup operand format using selected nybble 849 | STA ACMD ; save for later use 850 | AND #$03 ; lower 2 bits indicate number of bytes in operand 851 | STA LENGTH 852 | TYA ; restore original opcode 853 | AND #$8F ; mask bits to X000XXXX 854 | TAX ; save it 855 | TYA ; restore original opcode 856 | LDY #3 857 | CPX #$8A ; check if opcode = 1XXX1010 858 | BEQ GTFM4 859 | GTFM2 LSR A ; transform opcode into index for mnemonic table 860 | BCC GTFM4 861 | LSR A ; opcodes transformed as follows: 862 | GTFM3 LSR A ; 1XXX1010->00101XXX 863 | ORA #$20 ; XXXYYY01->00111XXX 864 | DEY ; XXXYYY10->00111XXX 865 | BNE GTFM3 ; XXXYY100->00110XXX 866 | INY ; XXXXX000->000XXXXX 867 | GTFM4 DEY 868 | BNE GTFM2 869 | RTS 870 | 871 | ; ----------------------------------------------------------------------------- 872 | ; extract and print packed mnemonics 873 | PROPXX TAY ; use index in accumulator to look up mnemonic 874 | LDA MNEML,Y ; and place a temporary copy in STORE 875 | STA STORE 876 | LDA MNEMR,Y 877 | STA STORE+1 878 | PRMN1 LDA #0 ; clear accumulator 879 | LDY #$05 ; shift 5 times 880 | PRMN2 ASL STORE+1 ; shift right byte 881 | ROL STORE ; rotate bits from right byte into left byte 882 | ROL A ; rotate bits from left byte into accumulator 883 | DEY ; next bit 884 | BNE PRMN2 ; loop until all bits shifted 885 | ADC #$3F ; calculate ascii code for letter by adding to '?' 886 | JSR CHROUT ; output letter 887 | DEX ; next letter 888 | BNE PRMN1 ; loop until all 3 letters are output 889 | JMP SPACE ; output space 890 | 891 | ; ----------------------------------------------------------------------------- 892 | ; read parameters 893 | RDPAR DEC CHRPNT ; back up one char 894 | GETPAR JSR RDVAL ; read the value 895 | BCS GTERR ; carry set indicates error 896 | JSR GOTCHR ; check previous character 897 | BNE CKTERM ; if it's not null, check if it's a valid separator 898 | DEC CHRPNT ; back up one char 899 | LDA DIGCNT ; get number of digits read 900 | BNE GETGOT ; found some digits 901 | BEQ GTNIL ; didn't find any digits 902 | CKTERM CMP #$20 ; space or comma are valid separators 903 | BEQ GETGOT ; anything else is an error 904 | CMP #"," 905 | BEQ GETGOT 906 | GTERR PLA ; encountered error 907 | PLA ; get rid of command vector pushed on stack 908 | JMP ERROR ; handle error 909 | GTNIL SEC ; set carry to indicate no parameter found 910 | .BYTE $24 ; BIT ZP opcode consumes next byte (CLC) 911 | GETGOT CLC ; clear carry to indicate paremeter returned 912 | LDA DIGCNT ; return number of digits in A 913 | RTS ; return to address pushed from vector table 914 | 915 | ; ----------------------------------------------------------------------------- 916 | ; read a value in the specified base 917 | RDVAL LDA #0 ; clear temp 918 | STA TMP0 919 | STA TMP0+1 920 | STA DIGCNT ; clear digit counter 921 | TXA ; save X and Y 922 | PHA 923 | TYA 924 | PHA 925 | RDVMOR JSR GETCHR ; get next character from input buffer 926 | BEQ RDNILK ; null at end of buffer 927 | CMP #$20 ; skip spaces 928 | BEQ RDVMOR 929 | LDX #3 ; check numeric base [$+&%] 930 | GNMODE CMP HIKEY,X 931 | BEQ GOTMOD ; got a match, set up base 932 | DEX 933 | BPL GNMODE ; check next base 934 | INX ; default to hex 935 | DEC CHRPNT ; back up one character 936 | GOTMOD LDY MODTAB,X ; get base value 937 | LDA LENTAB,X ; get bits per digit 938 | STA NUMBIT ; store bits per digit 939 | NUDIG JSR GETCHR ; get next char in A 940 | RDNILK BEQ RDNIL ; end of number if no more characters 941 | SEC 942 | SBC #$30 ; subtract ascii value of 0 to get numeric value 943 | BCC RDNIL ; end of number if character was less than 0 944 | CMP #$0A 945 | BCC DIGMOR ; not a hex digit if less than A 946 | SBC #$07 ; 7 chars between ascii 9 and A, so subtract 7 947 | CMP #$10 ; end of number if char is greater than F 948 | BCS RDNIL 949 | DIGMOR STA INDIG ; store the digit 950 | CPY INDIG ; compare base with the digit 951 | BCC RDERR ; error if the digit >= the base 952 | BEQ RDERR 953 | INC DIGCNT ; increment the number of digits 954 | CPY #10 955 | BNE NODECM ; skip the next part if not using base 10 956 | LDX #1 957 | DECLP1 LDA TMP0,X ; stash the previous 16-bit value for later use 958 | STA STASH,X 959 | DEX 960 | BPL DECLP1 961 | NODECM LDX NUMBIT ; number of bits to shift 962 | TIMES2 ASL TMP0 ; shift 16-bit value by specified number of bits 963 | ROL TMP0+1 964 | BCS RDERR ; error if we overflowed 16 bits 965 | DEX 966 | BNE TIMES2 ; shift remaining bits 967 | CPY #10 968 | BNE NODEC2 ; skip the next part if not using base 10 969 | ASL STASH ; shift the previous 16-bit value one bit left 970 | ROL STASH+1 971 | BCS RDERR ; error if we overflowed 16 bits 972 | LDA STASH ; add shifted previous value to current value 973 | ADC TMP0 974 | STA TMP0 975 | LDA STASH+1 976 | ADC TMP0+1 977 | STA TMP0+1 978 | BCS RDERR ; error if we overflowed 16 bits 979 | NODEC2 CLC 980 | LDA INDIG ; load current digit 981 | ADC TMP0 ; add current digit to low byte 982 | STA TMP0 ; and store result back in low byte 983 | TXA ; A=0 984 | ADC TMP0+1 ; add carry to high byte 985 | STA TMP0+1 ; and store result back in high byte 986 | BCC NUDIG ; get next digit if we didn't overflow 987 | RDERR SEC ; set carry to indicate error 988 | .BYTE $24 ; BIT ZP opcode consumes next byte (CLC) 989 | RDNIL CLC ; clear carry to indicate success 990 | STY NUMBIT ; save base of number 991 | PLA ; restore X and Y 992 | TAY 993 | PLA 994 | TAX 995 | LDA DIGCNT ; return number of digits in A 996 | RTS 997 | 998 | ; ----------------------------------------------------------------------------- 999 | ; print address 1000 | SHOWAD LDA TMP2 1001 | LDX TMP2+1 1002 | 1003 | WRADDR PHA ; save low byte 1004 | TXA ; put high byte in A 1005 | JSR WRTWO ; output high byte 1006 | PLA ; restore low byte 1007 | 1008 | WRBYTE JSR WRTWO ; output byte in A 1009 | 1010 | SPACE LDA #$20 ; output space 1011 | BNE FLIP 1012 | 1013 | CHOUT CMP #$0D ; output char with special handling of CR 1014 | BNE FLIP 1015 | CRLF LDA #$0D ; load CR in A 1016 | BIT $13 ; check default channel 1017 | BPL FLIP ; if high bit is clear output CR only 1018 | JSR CHROUT ; otherwise output CR+LF 1019 | LDA #$0A ; output LF 1020 | FLIP JMP CHROUT 1021 | 1022 | FRESH JSR CRLF ; output CR 1023 | LDA #$20 ; load space in A 1024 | JSR CHROUT 1025 | JMP SNCLR 1026 | 1027 | ; ----------------------------------------------------------------------------- 1028 | ; output two hex digits for byte 1029 | WRTWO STX SAVX ; save X 1030 | JSR ASCTWO ; get hex chars for byte in X (lower) and A (upper) 1031 | JSR CHROUT ; output upper nybble 1032 | TXA ; transfer lower to A 1033 | LDX SAVX ; restore X 1034 | JMP CHROUT ; output lower nybble 1035 | 1036 | ; ----------------------------------------------------------------------------- 1037 | ; convert byte in A to hex digits 1038 | ASCTWO PHA ; save byte 1039 | JSR ASCII ; do low nybble 1040 | TAX ; save in X 1041 | PLA ; restore byte 1042 | LSR A ; shift upper nybble down 1043 | LSR A 1044 | LSR A 1045 | LSR A 1046 | 1047 | ; convert low nybble in A to hex digit 1048 | ASCII AND #$0F ; clear upper nibble 1049 | CMP #$0A ; if less than A, skip next step 1050 | BCC ASC1 1051 | ADC #6 ; skip ascii chars between 9 and A 1052 | ASC1 ADC #$30 ; add ascii char 0 to value 1053 | RTS 1054 | 1055 | ; ----------------------------------------------------------------------------- 1056 | ; get prev char from input buffer 1057 | GOTCHR DEC CHRPNT 1058 | 1059 | ; get next char from input buffer 1060 | GETCHR STX SAVX 1061 | LDX CHRPNT ; get pointer to next char 1062 | LDA INBUFF,X ; load next char in A 1063 | BEQ NOCHAR ; null, :, or ? signal end of buffer 1064 | CMP #":" 1065 | BEQ NOCHAR 1066 | CMP #"?" 1067 | NOCHAR PHP 1068 | INC CHRPNT ; next char 1069 | LDX SAVX 1070 | PLP ; Z flag will signal last character 1071 | RTS 1072 | 1073 | ; ----------------------------------------------------------------------------- 1074 | ; copy TMP0 to TMP2 1075 | COPY12 LDA TMP0 ; low byte 1076 | STA TMP2 1077 | LDA TMP0+1 ; high byte 1078 | STA TMP2+1 1079 | RTS 1080 | 1081 | ; ----------------------------------------------------------------------------- 1082 | ; subtract TMP2 from TMP0 1083 | SUB12 SEC 1084 | LDA TMP0 ; subtract low byte 1085 | SBC TMP2 1086 | STA TMP0 1087 | LDA TMP0+1 1088 | SBC TMP2+1 ; subtract high byte 1089 | STA TMP0+1 1090 | RTS 1091 | 1092 | ; ----------------------------------------------------------------------------- 1093 | ; subtract from TMP0 1094 | SUBA1 LDA #1 ; shortcut to decrement by 1 1095 | SUBA2 STA SAVX ; subtrahend in accumulator 1096 | SEC 1097 | LDA TMP0 ; minuend in low byte 1098 | SBC SAVX 1099 | STA TMP0 1100 | LDA TMP0+1 ; borrow from high byte 1101 | SBC #0 1102 | STA TMP0+1 1103 | RTS 1104 | 1105 | ; ----------------------------------------------------------------------------- 1106 | ; subtract 1 from STORE 1107 | SUB13 SEC 1108 | LDA STORE 1109 | SBC #1 ; decrement low byte 1110 | STA STORE 1111 | LDA STORE+1 1112 | SBC #0 ; borrow from high byte 1113 | STA STORE+1 1114 | RTS 1115 | 1116 | ; ----------------------------------------------------------------------------- 1117 | ; add to TMP2 1118 | ADDA2 LDA #1 ; shortcut to increment by 1 1119 | BUMPAD2 CLC 1120 | ADC TMP2 ; add value in accumulator to low byte 1121 | STA TMP2 1122 | BCC BUMPEX 1123 | INC TMP2+1 ; carry to high byte 1124 | BUMPEX RTS 1125 | 1126 | ; ----------------------------------------------------------------------------- 1127 | ; subtract 1 from TMP2 1128 | SUB21 SEC 1129 | LDA TMP2 ; decrement low byte 1130 | SBC #1 1131 | STA TMP2 1132 | LDA TMP2+1 ; borrow from high byte 1133 | SBC #0 1134 | STA TMP2+1 1135 | RTS 1136 | 1137 | ; ----------------------------------------------------------------------------- 1138 | ; copy TMP0 to PC 1139 | COPY1P BCS CPY1PX ; do nothing if parameter is empty 1140 | LDA TMP0 ; copy low byte 1141 | LDY TMP0+1 ; copy high byte 1142 | STA PCL 1143 | STY PCH 1144 | CPY1PX RTS 1145 | 1146 | ; ----------------------------------------------------------------------------- 1147 | ; get start/end addresses and calc difference 1148 | GETDIF BCS GDIFX ; exit with error if no parameter given 1149 | JSR COPY12 ; save start address in TMP2 1150 | JSR GETPAR ; get end address in TMP0 1151 | BCS GDIFX ; exit with error if no parameter given 1152 | LDA TMP0 ; save end address in STASH 1153 | STA STASH 1154 | LDA TMP0+1 1155 | STA STASH+1 1156 | JSR SUB12 ; subtract start address from end address 1157 | LDA TMP0 1158 | STA STORE ; save difference in STORE 1159 | LDA TMP0+1 1160 | STA STORE+1 1161 | BCC GDIFX ; error if start address is after end address 1162 | CLC ; clear carry to indicate success 1163 | .BYTE $24 ; BIT ZP opcode consumes next byte (SEC) 1164 | GDIFX SEC ; set carry to indicate error 1165 | RTS 1166 | 1167 | ; ----------------------------------------------------------------------------- 1168 | ; convert base [$+&%] 1169 | CONVRT JSR RDPAR ; read a parameter 1170 | JSR FRESH ; next line and clear 1171 | LDA #"$" ; output $ sigil for hex 1172 | JSR CHROUT 1173 | LDA TMP0 ; load the 16-bit value entered 1174 | LDX TMP0+1 1175 | JSR WRADDR ; print it in 4 hex digits 1176 | JSR FRESH 1177 | LDA #"+" ; output + sigil for decimal 1178 | JSR CHROUT 1179 | JSR CVTDEC ; convert to BCD using hardware mode 1180 | LDA #0 ; clear digit counter 1181 | LDX #6 ; max digits + 1 1182 | LDY #3 ; bits per digit - 1 1183 | JSR NMPRNT ; print result without leading zeros 1184 | JSR FRESH ; next line and clear 1185 | LDA #"&" ; print & sigil for octal 1186 | JSR CHROUT 1187 | LDA #0 ; clear digit counter 1188 | LDX #8 ; max digits + 1 1189 | LDY #2 ; bits per digit - 1 1190 | JSR PRINUM ; output number 1191 | JSR FRESH ; next line and clear 1192 | LDA #"%" ; print % sigil for binary 1193 | JSR CHROUT 1194 | LDA #0 ; clear digit counter 1195 | LDX #$18 ; max digits + 1 1196 | LDY #0 ; bits per digit - 1 1197 | JSR PRINUM ; output number 1198 | JMP STRT ; back to mainloop 1199 | 1200 | ; ----------------------------------------------------------------------------- 1201 | ; convert binary to BCD 1202 | 1203 | CVTDEC JSR COPY12 ; copy value from TMP0 to TMP2 1204 | LDA #0 1205 | LDX #2 ; clear 3 bytes in work buffer 1206 | DECML1 STA U0AA0,X 1207 | DEX 1208 | BPL DECML1 1209 | LDY #16 ; 16 bits in input 1210 | PHP ; save status register 1211 | SEI ; make sure no interrupts occur with BCD enabled 1212 | SED 1213 | DECML2 ASL TMP2 ; rotate bytes out of input low byte 1214 | ROL TMP2+1 ; .. into high byte and carry bit 1215 | LDX #2 ; process 3 bytes 1216 | DECDBL LDA U0AA0,X ; load current value of byte 1217 | ADC U0AA0,X ; add it to itself plus the carry bit 1218 | STA U0AA0,X ; store it back in the same location 1219 | DEX ; decrement byte counter 1220 | BPL DECDBL ; loop until all bytes processed 1221 | DEY ; decrement bit counter 1222 | BNE DECML2 ; loop until all bits processed 1223 | PLP ; restore processor status 1224 | RTS 1225 | 1226 | ; load the input value and fall through to print it 1227 | PRINUM PHA ; save accumulator 1228 | LDA TMP0 ; copy input low byte to work buffer 1229 | STA U0AA0+2 1230 | LDA TMP0+1 ; copy input high byte to work buffer 1231 | STA U0AA0+1 1232 | LDA #0 ; clear overflow byte in work buffer 1233 | STA U0AA0 1234 | PLA ; restore accumulator 1235 | 1236 | ; print number in specified base without leading zeros 1237 | NMPRNT STA DIGCNT ; number of digits in accumulator 1238 | STY NUMBIT ; bits per digit passed in Y register 1239 | DIGOUT LDY NUMBIT ; get bits to process 1240 | LDA #0 ; clear accumulator 1241 | ROLBIT ASL U0AA0+2 ; shift bits out of low byte 1242 | ROL U0AA0+1 ; ... into high byte 1243 | ROL U0AA0 ; ... into overflow byte 1244 | ROL A ; ... into accumulator 1245 | DEY ; decrement bit counter 1246 | BPL ROLBIT ; loop until all bits processed 1247 | TAY ; check whether accumulator is 0 1248 | BNE NZERO ; if not, print it 1249 | CPX #1 ; have we output the max number of digits? 1250 | BEQ NZERO ; if not, print it 1251 | LDY DIGCNT ; how many digits have we output? 1252 | BEQ ZERSUP ; skip output if digit is 0 1253 | NZERO INC DIGCNT ; increment digit counter 1254 | ORA #$30 ; add numeric value to ascii '0' to get ascii char 1255 | JSR CHROUT ; output character 1256 | ZERSUP DEX ; decrement number of leading zeros 1257 | BNE DIGOUT ; next digit 1258 | RTS 1259 | 1260 | ; ----------------------------------------------------------------------------- 1261 | ; disk status/command [@] 1262 | DSTAT BNE CHGDEV ; if device address was given, use it 1263 | LDX #8 ; otherwise, default to 8 1264 | .BYTE $2C ; absolute BIT opcode consumes next word (LDX TMP0) 1265 | CHGDEV LDX TMP0 ; load device address from parameter 1266 | CPX #4 ; make sure device address is in range 4-31 1267 | BCC IOERR 1268 | CPX #32 1269 | BCS IOERR 1270 | STX TMP0 1271 | LDA #0 ; clear status 1272 | STA SATUS 1273 | STA FNLEN ; empty filename 1274 | JSR GETCHR ; get next character 1275 | BEQ INSTAT1 ; null, display status 1276 | DEC CHRPNT ; back up 1 char 1277 | CMP #"$" ; $, display directory 1278 | BEQ DIRECT 1279 | LDA TMP0 ; command specified device to listen 1280 | JSR LISTEN 1281 | LDA #$6F ; secondary address 15 (only low nybble used) 1282 | JSR SECOND 1283 | 1284 | ; send command to device 1285 | DCOMD LDX CHRPNT ; get next character from buffer 1286 | INC CHRPNT 1287 | LDA INBUFF,X 1288 | BEQ INSTAT ; break out of loop if it's null 1289 | JSR CIOUT ; otherwise output it to the serial bus 1290 | BCC DCOMD ; unconditional loop: CIOUT clears carry before RTS 1291 | 1292 | ; get device status 1293 | INSTAT JSR UNLSN ; command device to unlisten 1294 | INSTAT1 JSR CRLF ; new line 1295 | LDA TMP0 ; load device address 1296 | JSR TALK ; command device to talk 1297 | LDA #$6F ; secondary address 15 (only low nybble used) 1298 | JSR TKSA 1299 | RDSTAT JSR ACPTR ; read byte from serial bus 1300 | JSR CHROUT ; print it 1301 | CMP #$0D ; if the byte is CR, exit loop 1302 | BEQ DEXIT 1303 | LDA SATUS ; check status 1304 | AND #$BF ; ignore EOI bit 1305 | BEQ RDSTAT ; if no errors, read next byte 1306 | DEXIT JSR UNTLK ; command device to stop talking 1307 | JMP STRT ; back to mainloop 1308 | IOERR JMP ERROR ; handle error 1309 | 1310 | ; get directory 1311 | DIRECT LDA TMP0 ; load device address 1312 | JSR LISTEN ; command device to listen 1313 | LDA #$F0 ; secondary address 0 (only low nybble used) 1314 | JSR SECOND 1315 | LDX CHRPNT ; get index of next character 1316 | DIR2 LDA INBUFF,X ; get next character from buffer 1317 | BEQ DIR3 ; break if it's null 1318 | JSR CIOUT ; send character to device 1319 | INX ; increment characer index 1320 | BNE DIR2 ; loop if it hasn't wrapped to zero 1321 | DIR3 JSR UNLSN ; command device to unlisten 1322 | JSR CRLF ; new line 1323 | LDA TMP0 ; load device address 1324 | PHA ; save on stack 1325 | JSR TALK ; command device to talk 1326 | LDA #$60 ; secondary address 0 (only low nybble used) 1327 | JSR TKSA 1328 | LDY #3 ; read 3 16-bit values from device 1329 | DIRLIN STY STORE ; ignore the first 2; 3rd is file size 1330 | DLINK JSR ACPTR ; read low byte from device 1331 | STA TMP0 ; store it 1332 | LDA SATUS ; check status 1333 | BNE DREXIT ; exit if error or eof occurred 1334 | JSR ACPTR ; read high byte from device 1335 | STA TMP0+1 ; store it 1336 | LDA SATUS ; check status 1337 | BNE DREXIT ; exit if error or eof cocurred 1338 | DEC STORE ; decrement byte count 1339 | BNE DLINK ; loop if bytes remain 1340 | JSR CVTDEC ; convert last 16-bit value to decimal 1341 | LDA #0 ; clear digit count 1342 | LDX #6 ; max 6 digits 1343 | LDY #3 ; 3 bits per digit 1344 | JSR NMPRNT ; output number 1345 | LDA #" " ; output space 1346 | JSR CHROUT 1347 | DNAME JSR ACPTR ; get a filename character from the device 1348 | BEQ DMORE ; if it's null, break out of loop 1349 | LDX SATUS ; check for errors or eof 1350 | BNE DREXIT ; if found exit early 1351 | JSR CHROUT ; output character 1352 | CLC 1353 | BCC DNAME ; unconditional branch to read next char 1354 | DMORE JSR CRLF 1355 | JSR STOP ; check for stop key 1356 | BEQ DREXIT ; exit early if pressed 1357 | JSR GETIN ; pause if a key was pressed 1358 | BEQ NOPAWS 1359 | PAWS JSR GETIN ; wait until another key is pressed 1360 | BEQ PAWS 1361 | NOPAWS LDY #2 1362 | BNE DIRLIN ; unconditional branch to read next file 1363 | DREXIT JSR UNTLK ; command device to untalk 1364 | PLA ; restore accumulator 1365 | JSR LISTEN ; command device to listen 1366 | LDA #$E0 ; secondary address 0 (only low nybble is used) 1367 | JSR SECOND 1368 | JSR UNLSN ; command device to unlisten 1369 | JMP STRT ; back to mainloop 1370 | 1371 | ; ----------------------------------------------------------------------------- 1372 | ; print and clear routines 1373 | CLINE JSR CRLF ; send CR+LF 1374 | JMP SNCLR ; clear line 1375 | SNDCLR JSR SNDMSG 1376 | SNCLR LDY #$28 ; loop 40 times 1377 | SNCLP LDA #$20 ; output space character 1378 | JSR CHROUT 1379 | LDA #$14 ; output delete character 1380 | JSR CHROUT 1381 | DEY 1382 | BNE SNCLP 1383 | RTS 1384 | 1385 | ; ----------------------------------------------------------------------------- 1386 | ; display message from table 1387 | SNDMSG LDA MSGBAS,Y ; Y contains offset in msg table 1388 | PHP 1389 | AND #$7F ; strip high bit before output 1390 | JSR CHOUT 1391 | INY 1392 | PLP 1393 | BPL SNDMSG ; loop until high bit is set 1394 | RTS 1395 | 1396 | ; ----------------------------------------------------------------------------- 1397 | ; message table; last character has high bit set 1398 | MSGBAS =* 1399 | MSG2 .BYTE $0D ; header for registers 1400 | .TEXT " PC SR AC XR YR SP V1.2" 1401 | .BYTE $0D+$80 1402 | MSG3 .BYTE $1D,$3F+$80 ; syntax error: move right, display "?" 1403 | MSG4 .TEXT "..SYS" ; SYS call to enter monitor 1404 | .BYTE $20+$80 1405 | MSG5 .BYTE $3A,$12+$80 ; ":" then RVS ON for memory ASCII dump 1406 | MSG6 .TEXT " ERRO" ; I/O error: display " ERROR" 1407 | .BYTE "R"+$80 1408 | MSG7 .BYTE $41,$20+$80 ; assemble next instruction: "A " + addr 1409 | MSG8 .TEXT " " ; pad non-existent byte: skip 3 spaces 1410 | .BYTE $20+$80 1411 | 1412 | ; ----------------------------------------------------------------------------- 1413 | ; addressing mode table - nybbles provide index into MODE2 table 1414 | ; for opcodes XXXXXXY0, use XXXXXX as index into table 1415 | ; for opcodes WWWXXY01 use $40 + XX as index into table 1416 | ; use right nybble if Y=0; use left nybble if Y=1 1417 | 1418 | MODE .BYTE $40,$02,$45,$03 ; even opcodes 1419 | .BYTE $D0,$08,$40,$09 1420 | .BYTE $30,$22,$45,$33 1421 | .BYTE $D0,$08,$40,$09 1422 | .BYTE $40,$02,$45,$33 1423 | .BYTE $D0,$08,$40,$09 1424 | .BYTE $40,$02,$45,$B3 1425 | .BYTE $D0,$08,$40,$09 1426 | .BYTE $00,$22,$44,$33 1427 | .BYTE $D0,$8C,$44,$00 1428 | .BYTE $11,$22,$44,$33 1429 | .BYTE $D0,$8C,$44,$9A 1430 | .BYTE $10,$22,$44,$33 1431 | .BYTE $D0,$08,$40,$09 1432 | .BYTE $10,$22,$44,$33 1433 | .BYTE $D0,$08,$40,$09 1434 | .BYTE $62,$13,$78,$A9 ; opcodes ending in 01 1435 | 1436 | ; addressing mode format definitions indexed by nybbles from MODE table 1437 | 1438 | ; left 6 bits define which characters appear in the assembly operand 1439 | ; left 3 bits are before the address; next 3 bits are after 1440 | 1441 | ; right-most 2 bits define length of binary operand 1442 | 1443 | ; index 654 321 1444 | ; 1st character $(# ,), 1445 | ; 2nd character $$ X Y length format idx mode 1446 | MODE2 .BYTE $00 ; 000 000 00 0 error 1447 | .BYTE $21 ; 001 000 01 #$00 1 immediate 1448 | .BYTE $81 ; 100 000 01 $00 2 zero-page 1449 | .BYTE $82 ; 100 000 10 $0000 3 absolute 1450 | .BYTE $00 ; 000 000 00 4 implied 1451 | .BYTE $00 ; 000 000 00 5 accumulator 1452 | .BYTE $59 ; 010 110 01 ($00,X) 6 indirect,X 1453 | .BYTE $4D ; 010 011 01 ($00),Y 7 indirect,Y 1454 | .BYTE $91 ; 100 100 01 $00,X 8 zero-page,X 1455 | .BYTE $92 ; 100 100 10 $0000,X 9 absolute,X 1456 | .BYTE $86 ; 100 001 10 $0000,Y A absolute,Y 1457 | .BYTE $4A ; 010 010 10 ($0000) B indirect 1458 | .BYTE $85 ; 100 001 01 $00,Y C zero-page,Y 1459 | .BYTE $9D ; 100 111 01 $0000* D relative 1460 | 1461 | ; * relative is special-cased so format bits don't match 1462 | 1463 | 1464 | ; character lookup tables for the format definitions in MODE2 1465 | 1466 | CHAR1 .BYTE $2C,$29,$2C ; "," ")" "," 1467 | .BYTE $23,$28,$24 ; "#" "(" "$" 1468 | 1469 | CHAR2 .BYTE $59,$00,$58 ; "Y" 0 "X" 1470 | .BYTE $24,$24,$00 ; "$" "$" 0 1471 | 1472 | ; ----------------------------------------------------------------------------- 1473 | ; 3-letter mnemonics packed into two bytes (5 bits per letter) 1474 | 1475 | ; left 8 bits 1476 | ; XXXXX000 opcodes 1477 | MNEML .BYTE $1C,$8A,$1C,$23 ; BRK PHP BPL CLC 1478 | .BYTE $5D,$8B,$1B,$A1 ; JSR PLP BMI SEC 1479 | .BYTE $9D,$8A,$1D,$23 ; RTI PHA BVC CLI 1480 | .BYTE $9D,$8B,$1D,$A1 ; RTS PLA BVS SEI 1481 | .BYTE $00,$29,$19,$AE ; ??? DEY BCC TYA 1482 | .BYTE $69,$A8,$19,$23 ; LDY TAY BCS CLV 1483 | .BYTE $24,$53,$1B,$23 ; CPY INY BNE CLD 1484 | .BYTE $24,$53,$19,$A1 ; CPX INX BEQ SED 1485 | ; XXXYY100 opcodes 1486 | .BYTE $00,$1A,$5B,$5B ; ??? BIT JMP JMP 1487 | .BYTE $A5,$69,$24,$24 ; STY LDY CPY CPX 1488 | ; 1XXX1010 opcodes 1489 | .BYTE $AE,$AE,$A8,$AD ; TXA TXS TAX TSX 1490 | .BYTE $29,$00,$7C,$00 ; DEX ??? NOP ??? 1491 | ; XXXYYY10 opcodes 1492 | .BYTE $15,$9C,$6D,$9C ; ASL ROL LSR ROR 1493 | .BYTE $A5,$69,$29,$53 ; STX LDX DEC INC 1494 | ; XXXYYY01 opcodes 1495 | .BYTE $84,$13,$34,$11 ; ORA AND EOR ADC 1496 | .BYTE $A5,$69,$23,$A0 ; STA LDA CMP SBC 1497 | 1498 | ; right 7 bits, left justified 1499 | ; XXXXX000 opcodes 1500 | MNEMR .BYTE $D8,$62,$5A,$48 ; BRK PHP BPL CLC 1501 | .BYTE $26,$62,$94,$88 ; JSR PLP BMI SEC 1502 | .BYTE $54,$44,$C8,$54 ; RTI PHA BVC CLI 1503 | .BYTE $68,$44,$E8,$94 ; RTS PLA BVS SEI 1504 | .BYTE $00,$B4,$08,$84 ; ??? DEY BCC TYA 1505 | .BYTE $74,$B4,$28,$6E ; LDY TAY BCS CLV 1506 | .BYTE $74,$F4,$CC,$4A ; CPY INY BNE CLD 1507 | .BYTE $72,$F2,$A4,$8A ; CPX INX BEQ SED 1508 | ; XXXYY100 opcodes 1509 | .BYTE $00,$AA,$A2,$A2 ; ??? BIT JMP JMP 1510 | .BYTE $74,$74,$74,$72 ; STY LDY CPY CPX 1511 | ; 1XXX1010 opcodes 1512 | .BYTE $44,$68,$B2,$32 ; TXA TXS TAX TSX 1513 | .BYTE $B2,$00,$22,$00 ; DEX ??? NOP ??? 1514 | ; XXXYYY10 opcodes 1515 | .BYTE $1A,$1A,$26,$26 ; ASL ROL LSR ROR 1516 | .BYTE $72,$72,$88,$C8 ; STX LDX DEC INC 1517 | ; XXXYYY01 opcodes 1518 | .BYTE $C4,$CA,$26,$48 ; ORA AND EOR ADC 1519 | .BYTE $44,$44,$A2,$C8 ; STA LDA CMP SBC 1520 | .BYTE $0D,$20,$20,$20 1521 | 1522 | ; ----------------------------------------------------------------------------- 1523 | ; single-character commands 1524 | KEYW .TEXT "ACDFGHJMRTX@.>;" 1525 | HIKEY .TEXT "$+&%LSV" 1526 | KEYTOP =* 1527 | 1528 | ; vectors corresponding to commands above 1529 | KADDR .WORD ASSEM-1,COMPAR-1,DISASS-1,FILL-1 1530 | .WORD GOTO-1,HUNT-1,JSUB-1,DSPLYM-1 1531 | .WORD DSPLYR-1,TRANS-1,EXIT-1,DSTAT-1 1532 | .WORD ASSEM-1,ALTM-1,ALTR-1 1533 | 1534 | ; ----------------------------------------------------------------------------- 1535 | MODTAB .BYTE $10,$0A,$08,02 ; modulo number systems 1536 | LENTAB .BYTE $04,$03,$03,$01 ; bits per digit 1537 | 1538 | LINKAD .WORD BREAK ; address of brk handler 1539 | SUPAD .WORD SUPER ; address of entry point 1540 | --------------------------------------------------------------------------------