123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- #!/usr/bin/env python
- # -*- coding: UTF-8 -*-
- # List a ZX81 program.
- # Written by Pedro Gimeno Fortea. Donated to the public domain.
- import sys
- chars = [
- ' ',
- '▘', # Top Left
- '▝', # Top Right
- '▀', # Top
- '▖', # Bottom Left
- '▌', # Left
- '▞', # Top Right & Bottom Left
- '▛', # Inverse Bottom Right
- '▒', # Checkerboard 50% grey
- '🮎', # Half Checkerboard Up
- '🮏', # Half Checkerboard Down
- '"', '£', '$', ':', '?', '(', ')', '>', '<', '=', '+', '-', '*', '/', ';',
- ',', '.',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
- 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- 'RND', 'INKEY$', 'PI',
- '{x43}', '{x44}', '{x45}', '{x46}', '{x47}', '{x48}', '{x49}', '{x4A}',
- '{x4B}', '{x4C}', '{x4D}', '{x4E}', '{x4F}', '{x50}', '{x51}', '{x52}',
- '{x53}', '{x54}', '{x55}', '{x56}', '{x57}', '{x58}', '{x59}', '{x5A}',
- '{x5B}', '{x5C}', '{x5D}', '{x5E}', '{x5F}', '{x60}', '{x61}', '{x62}',
- '{x63}', '{x64}', '{x65}', '{x66}', '{x67}', '{x68}', '{x69}', '{x6A}',
- '{x6B}', '{x6C}', '{x6D}', '{x6E}', '{x6F}',
- '⇧', '⇩', '⇦', '⇨', '{GRAPH}', '{EDIT}',
- '↲',
- '⌫', '{K/L MODE}', '{FUNCTION}',
- '{x7A}', '{x7B}', '{x7C}', '{x7D}', '{#}', '{CURSOR}',
- '█', '▟', '▙', '▄', '▜', '▐', '▚', '▗', '🮐','🮒','🮑',
- '{inv"}', '{inv£}', '{inv$}', '{inv:}', '{inv?}', '{inv(}', '{inv)}',
- '{inv>}', '{inv<}', '{inv=}', '{inv+}', '{inv-}', '{inv*}', '{inv/}',
- '{inv;}', '{inv,}', '{inv.}', '{inv0}', '{inv1}', '{inv2}', '{inv3}',
- '{inv4}', '{inv5}', '{inv6}', '{inv7}', '{inv8}', '{inv9}', '{invA}',
- '{invB}', '{invC}', '{invD}', '{invE}', '{invF}', '{invG}', '{invH}',
- '{invI}', '{invJ}', '{invK}', '{invL}', '{invM}', '{invN}', '{invO}',
- '{invP}', '{invQ}', '{invR}', '{invS}', '{invT}', '{invU}', '{invV}',
- '{invW}', '{invX}', '{invY}', '{invZ}',
- '""', 'AT', 'TAB', '{xC3}', 'CODE', 'VAL', 'LEN', 'SIN', 'COS', 'TAN',
- 'ASN', 'ACS', 'ATN', 'LN', 'EXP', 'INT', 'SQR', 'SGN', 'ABS', 'PEEK',
- 'USR', 'STR$', 'CHR$', 'NOT', '**', 'OR', 'AND', '<=', '>=', '<>', 'THEN',
- 'TO', 'STEP', 'LPRINT', 'LLIST', 'STOP', 'SLOW', 'FAST', 'NEW', 'SCROLL',
- 'CONT', 'DIM', 'REM', 'FOR', 'GOTO', 'GOSUB', 'INPUT', 'LOAD', 'LIST',
- 'LET', 'PAUSE', 'NEXT', 'POKE', 'PRINT', 'PLOT', 'RUN', 'SAVE', 'RAND',
- 'IF', 'CLS', 'UNPLOT', 'CLEAR', 'RETURN', 'COPY'
- ]
- # Without bytearray, this is needed:
- #if sys.hexversion < 0x03000000:
- # print("NOTE: Python 3 required")
- # Probably because Python3 treats bytes[i] as int, while Python2 treats
- # it as bytes, while both treat bytearray[i] as int.
- if len(sys.argv) < 2:
- print("Usage: %s <filename> [hilite]" % sys.argv[0])
- sys.exit(0)
- f = open(sys.argv[1], 'rb')
- outf = sys.stdout
- hilite = False
- inv = ''
- bold = ''
- norm = ''
- if len(sys.argv) > 2:
- # Bold tokens, reverse video for inverse chars.
- # Requires terminal supporting ISO-6429 subset.
- # See console_codes(4)
- hilite = True
- inv = '\x1B[7m'
- bold = '\x1B[1m'
- norm = '\x1B[0m'
- for i in range(0x40):
- chars[i+0x80] = inv + chars[i] + norm
- try:
- # Both work in Python 3. Only the second lets the rest of the
- # program work in Python 2.
- # buf = f.read(65556)
- buf = bytearray(f.read(65556))
- lineptr = 0x74
- LeadingSpc = False
- while lineptr < len(buf):
- if buf[lineptr] >= 64:
- break
- linenum = buf[lineptr]*256 + buf[lineptr+1]
- linelen = buf[lineptr+2] + 256*buf[lineptr+3]
- lineptr += 4
- if linenum > 9999:
- outf.write(chars[linenum//1000+28] + str(linenum)[-3:] + ' ')
- else:
- outf.write('%4d ' % linenum)
- charptr = lineptr
- lineptr += linelen
- LeadingSpc = False # Represents the opposite of bit 0 of FLAGS
- while True:
- c = buf[charptr]
- if charptr+1 == lineptr:
- if c != 118:
- outf.write(chars[c])
- outf.write('{NO EOL}')
- break
- if c == 126: # FP number
- charptr += 6
- continue
- if c & 64: # a token?
- # Carry from the TOKEN-ADD (L0975) subroutine is the key to
- # knowing whether to obey LeadingSpc (see TOKENS, L094B).
- # Returns C if bit 0 of FLAGS should be obeyed.
- # TOKEN-ADD maps C0-FF to 0-3F, then:
- # if result >= 0x43: no leading space (097F)
- # else if result >= 0x40: no leading space (0985, C set)
- # else if result < 0x18: no leading space (0990)
- # else if first token char < 0x1C: no leading space (0998)
- # else obey the leading space flag
- # If result < 0x18 then it originally was < 0xD8, so the
- # above rules translate to: obey only if c >= 0xD8 and the
- # token begins with alphanumeric characters.
- if c >= 0xD8 and ('0' <= chars[c] <= '9'
- or 'A' <= chars[c] <= 'Z'
- ) and LeadingSpc:
- outf.write(' ') # Leading space
- LeadingSpc = False
- outf.write(bold + chars[c] + norm)
- else:
- outf.write(chars[c])
- if c:
- # Unless we just printed a space, next token should have a
- # leading one
- LeadingSpc = True
- # Trailing space
- if buf[charptr] >= 192: # RND, INKEY$, PI have no trailing space
- c = chars[buf[charptr]][0]
- # Check if alphanum or $ (this skips ** "" >= etc)
- if c == '$' or '0' <= c <= '9' or 'A' <= c <= 'Z':
- outf.write(' ')
- LeadingSpc = False
- charptr += 1
- outf.write('\n')
- finally:
- f.close()
|